From af64f8e9e3c9de767415eccacbd06b6b34c46465 Mon Sep 17 00:00:00 2001 From: rublon-bwi <134260122+rublon-bwi@users.noreply.github.com> Date: Fri, 7 Mar 2025 11:41:12 +0100 Subject: [PATCH 01/37] Bwi/v2.2.0 (#16) * Remove dynamic memory usage from core * Refacor status check to use json pointers * Move access token to session * Remove code duplication * Fix compile warnings from rapidjson sources * Add 'interactive mode option to session configuration * Implement non interactive mode connector * Add 'non interactove' implementation * Apply rapidjson patch * Build on all cores * Rename build script * Split configure and build steps * Add scripts for building all images * Change bash to python for build scripts * Stop printing methods name in non interactive mode * Add trace log level, adn more params to init message * Fix build * Fix non interactive method selection and refactor vagrant files for debian like systems * Refactor log messages * Remove exces dependencies from vagrant configuration files * Fixed vagrantfiles * Added repo for rhel * Add nonInteractiveMode option * Added instalation script for pubkey * Fixed pubkey install script and postrm for rhel --- .gitignore | 1 + CMakeLists.txt | 31 ++- PAM/ssh/CMakeLists.txt | 15 +- .../rublon/authentication_step_interface.hpp | 11 + PAM/ssh/include/rublon/check_application.hpp | 8 +- PAM/ssh/include/rublon/configuration.hpp | 7 +- PAM/ssh/include/rublon/core_handler.hpp | 14 +- .../include/rublon/core_handler_interface.hpp | 2 +- PAM/ssh/include/rublon/curl.hpp | 1 - PAM/ssh/include/rublon/error.hpp | 21 +- PAM/ssh/include/rublon/init.hpp | 44 ++-- PAM/ssh/include/rublon/json.hpp | 2 +- PAM/ssh/include/rublon/method/OTP.hpp | 1 - PAM/ssh/include/rublon/method/PUSH.hpp | 1 - PAM/ssh/include/rublon/method/SMS.hpp | 2 +- PAM/ssh/include/rublon/method/YOTP.hpp | 3 +- .../include/rublon/method/method_select.hpp | 189 ++++++++++++------ .../rublon/method/passcode_based_auth.hpp | 22 +- .../rublon/method/websocket_based_auth.hpp | 38 ++-- PAM/ssh/include/rublon/pam.hpp | 5 +- PAM/ssh/include/rublon/session.hpp | 35 +++- PAM/ssh/include/rublon/utils.hpp | 86 ++++---- PAM/ssh/include/rublon/websockets.hpp | 12 +- PAM/ssh/lib/pam.cpp | 9 +- PAM/ssh/patches/rapidjson.patch | 51 +++++ build.sh | 11 + build_all.py | 136 +++++++++++++ build_all_packages.sh | 9 - build_package.sh | 9 - configure.sh | 7 + helpers/centos-base.repo | 31 +++ helpers/centos-base_9.repo | 31 +++ os/alma/8/Vagrantfile | 50 ++--- os/alma/9/Vagrantfile | 46 ++--- os/centos/stream9/Dockerfile | 7 +- os/centos/stream9/Vagrantfile | 29 ++- os/debian/11/Dockerfile | 4 +- os/debian/11/Vagrantfile | 69 +++---- os/debian/12/Dockerfile | 4 +- os/debian/12/Vagrantfile | 59 +++--- os/opensuse/15sp3/Vagrantfile | 32 ++- os/rhel/8/Dockerfile | 3 +- os/rhel/8/Vagrantfile | 53 ++--- os/rhel/9/Vagrantfile | 49 +++-- os/rocky_linux/Vagrantfile | 47 +++-- os/ubuntu/20.04/Dockerfile | 3 +- os/ubuntu/20.04/Vagrantfile | 70 +++---- os/ubuntu/22.04/Dockerfile | 3 +- os/ubuntu/22.04/Vagrantfile | 61 +++--- os/ubuntu/24.04/Dockerfile | 3 +- os/ubuntu/24.04/Vagrantfile | 66 +++--- pack.cmake | 7 +- service/01-rublon-ssh_pubkey.conf.default | 2 +- service/helpers/postinst_pubkey | 25 --- service/helpers/postrm_rhel | 1 + service/helpers/pubkey_install_deb | 37 ---- service/inst_pubkey.sh | 17 ++ service/inst_pubkey_rhel_8.sh | 15 ++ service/inst_pubkey_rhel_9.sh | 17 ++ service/postinst_pubkey | 41 ---- service/pubkey_install_deb | 37 ---- 61 files changed, 976 insertions(+), 726 deletions(-) create mode 100644 PAM/ssh/patches/rapidjson.patch create mode 100755 build.sh create mode 100644 build_all.py delete mode 100755 build_all_packages.sh delete mode 100755 build_package.sh create mode 100755 configure.sh create mode 100644 helpers/centos-base.repo create mode 100644 helpers/centos-base_9.repo delete mode 100644 service/helpers/postinst_pubkey delete mode 100644 service/helpers/pubkey_install_deb create mode 100644 service/inst_pubkey.sh create mode 100644 service/inst_pubkey_rhel_8.sh create mode 100644 service/inst_pubkey_rhel_9.sh delete mode 100644 service/postinst_pubkey delete mode 100644 service/pubkey_install_deb diff --git a/.gitignore b/.gitignore index 7a56b55..30b284c 100755 --- a/.gitignore +++ b/.gitignore @@ -77,3 +77,4 @@ CMakeLists.txt.user* # Output files build/ +_tmp/ diff --git a/CMakeLists.txt b/CMakeLists.txt index ffe7e4d..a511b30 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(CTest) include(GNUInstallDirs) set(PROJECT_VERSION_MAJOR 2) -set(PROJECT_VERSION_MINOR 1) +set(PROJECT_VERSION_MINOR 2) set(PROJECT_VERSION_PATCH 0) set(RUBLON_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") @@ -17,6 +17,9 @@ set(CMAKE_POSITION_INDEPENDENT_CODE YES) add_compile_options(-Wall -Wextra -Wno-format-security) +# add_compile_options(-g -fsanitize=address,undefined,float-divide-by-zero,float-cast-overflow,null -fsanitize-address-use-after-scope -fno-sanitize-recover=all -fno-sanitize=alignment -fno-omit-frame-pointer) +# add_link_options(-g -fsanitize=address,undefined,float-divide-by-zero,float-cast-overflow,null -fsanitize-address-use-after-scope -fno-sanitize-recover=all -fno-sanitize=alignment -fno-omit-frame-pointer) + add_compile_definitions(RUBLON_VERSION_STRING="${RUBLON_VERSION_STRING}") option(ENABLE_TESTS "Enable tests" OFF) @@ -25,13 +28,16 @@ add_custom_target(INSTSCRIPTS_IDE SUORCES ${CMAKE_CURRENT_LIST_DIR}/service/help execute_process ( COMMAND bash -c "awk -F= '/^ID=/{print $2}' /etc/os-release |tr -d '\n' | tr -d '\"'" - OUTPUT_VARIABLE outOS + RESULT_VARIABLE outOS ) + +if ( ${outOS} MATCHES "ubuntu" OR ${outOS} MATCHES "debian" OR ${outOS} MATCHES "FREEBSD" ) install( FILES - ${CMAKE_CURRENT_LIST_DIR}/rsc/rublon.config.defaults + ${CMAKE_CURRENT_LIST_DIR}/rsc/rublon.config.defaults ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh_pubkey.conf.default ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh.conf.default + ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey.sh DESTINATION share/rublon COMPONENT @@ -41,15 +47,20 @@ install( OWNER_WRITE GROUP_READ ) - -if (NOT ${outOS} MATCHES "ubuntu" OR NOT ${outOS} MATCHES "debian" OR NOT ${outOS} MATCHES "FREEBSD") +else () install( FILES - ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.mod - ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.pp - ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.te - ${CMAKE_CURRENT_LIST_DIR}/service/pam_service.txt - ${CMAKE_CURRENT_LIST_DIR}/service/rublon_veritas + ${CMAKE_CURRENT_LIST_DIR}/rsc/rublon.config.defaults + ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh_pubkey.conf.default + ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh.conf.default + ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.mod + ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.pp + ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.te + ${CMAKE_CURRENT_LIST_DIR}/service/pam_service.txt + ${CMAKE_CURRENT_LIST_DIR}/service/rublon_veritas + ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey_rhel_9.sh + ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey_rhel_8.sh + ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey.sh DESTINATION share/rublon COMPONENT diff --git a/PAM/ssh/CMakeLists.txt b/PAM/ssh/CMakeLists.txt index 94cee9c..26dad44 100755 --- a/PAM/ssh/CMakeLists.txt +++ b/PAM/ssh/CMakeLists.txt @@ -79,6 +79,7 @@ set(LWS_UNIX_SOCK OFF) set(LWS_WITH_DIR OFF) set(LWS_WITH_FILE_OPS OFF) +set(LWS_FOR_GITOHASHI OFF) set(LWS_WITH_HTTP2 OFF) set(LWS_WITH_HTTP_BASIC_AUTH OFF) set(LWS_WITH_JPEG OFF) @@ -94,6 +95,9 @@ set(LWS_WITH_SECURE_STREAMS OFF) set(LWS_WITH_WOL OFF) set(LWS_WITH_UPNG OFF) set(LWS_WITH_UDP OFF) +set(LWS_WITH_HTTP_STREAM_COMPRESSION OFF) +set(LWS_WITH_HTTP_BROTLI OFF) +set(LWS_WITH_ZLIB OFF) set(RAPIDJSON_BUILD_DOC OFF CACHE BOOL "" FORCE) set(RAPIDJSON_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) @@ -114,10 +118,17 @@ FetchContent_Declare( FetchContent_MakeAvailable(libwebsockets) + +set(RAPIDJSON_BUILD_DOC OFF) +set(RAPIDJSON_BUILD_EXAMPLES OFF) +set(RAPIDJSON_BUILD_TESTS OFF) +set(RAPIDJSON_HAS_STDSTRING OFF) + FetchContent_Declare( RapidJSON - URL https://github.com/Tencent/rapidjson/archive/refs/tags/v1.1.0.zip - URL_HASH MD5=ceb1cf16e693a3170c173dc040a9d2bd + URL https://github.com/Tencent/rapidjson/archive/refs/tags/v1.1.0.zip + URL_HASH MD5=ceb1cf16e693a3170c173dc040a9d2bd + PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_LIST_DIR}/patches/rapidjson.patch ) if(NOT RapidJSON_POPULATED) diff --git a/PAM/ssh/include/rublon/authentication_step_interface.hpp b/PAM/ssh/include/rublon/authentication_step_interface.hpp index 2ae65b4..63f7ad3 100644 --- a/PAM/ssh/include/rublon/authentication_step_interface.hpp +++ b/PAM/ssh/include/rublon/authentication_step_interface.hpp @@ -29,6 +29,17 @@ class AuthenticationStep { auto & alloc = body.GetAllocator(); body.AddMember("accessToken", Value{token.data(), static_cast< unsigned >(token.length()), alloc}, alloc); } + + template < typename Hander_t > + tl::expected< AuthenticationStatus, Error > waitForCoreConfirmation(const CoreHandlerInterface< Hander_t > & eventListener) const { + auto event = eventListener.listen(); + if(event.status == transactionConfirmed ){ + log(LogLevel::Debug, " Transaction confirmed"); + return AuthenticationStatus{AuthenticationStatus::Action::Confirmed, std::string{event.accessToken.value().c_str()}}; + } + log(LogLevel::Debug, " Transaction denied"); + return AuthenticationStatus{AuthenticationStatus::Action::Denied}; + } }; } // namespace rublon diff --git a/PAM/ssh/include/rublon/check_application.hpp b/PAM/ssh/include/rublon/check_application.hpp index 648ee89..c038c75 100644 --- a/PAM/ssh/include/rublon/check_application.hpp +++ b/PAM/ssh/include/rublon/check_application.hpp @@ -59,7 +59,7 @@ class Status { std::ifstream ifs{_statusFilePath.data()}; if(!ifs.is_open()) { - /// TODO handle no file error + return; } rapidjson::IStreamWrapper isw{ifs}; @@ -67,7 +67,7 @@ class Status { } void updateAppVersion(std::string_view newVersion) { - RapidJSONPMRStackAlloc< 128 > stackAlloc; + RapidJSONPMRStackAlloc< 512 > stackAlloc; auto jsonPointer = JSONPointer{_appVersionKey.data(), &stackAlloc}; auto version = jsonPointer.Get(_data); if(not version || version->GetString() != newVersion) { @@ -77,7 +77,7 @@ class Status { } void updateSystemVersion(std::string_view system) { - RapidJSONPMRStackAlloc< 128 > stackAlloc; + RapidJSONPMRStackAlloc< 512 > stackAlloc; auto jsonPointer = JSONPointer{_paramSystemName.data(), &stackAlloc}; auto version = jsonPointer.Get(_data); if(not version || version->GetString() != system) { @@ -88,7 +88,7 @@ class Status { void updateSSHDConfig() { using namespace std::string_view_literals; - constexpr auto keys = make_array("authenticationmethods"sv, + constexpr auto keys = make_array("authenticationmethods"sv, "challengeresponseauthentication"sv, "kbdinteractiveauthentication"sv, "logingracetime"sv, diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 6404f92..6e8ae4a 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -27,6 +27,7 @@ class Configuration { bool logging{}; bool autopushPrompt{}; FailMode failMode{}; + bool nonInteractiveMode{}; }; namespace { @@ -138,7 +139,7 @@ constexpr auto make_entry(const char * name, const char * defaultValue) { return Entry{name, defaultValue, Entry::make_read_function< member >()}; } -constexpr static inline std::array< Entry, 8 > configurationVariables = { // +constexpr static inline std::array< Entry, 9 > configurationVariables = { // make_entry< &Configuration::logging >("logging", "true"), make_entry< &Configuration::systemToken >("systemToken", nullptr), make_entry< &Configuration::secretKey >("secretKey", nullptr), @@ -146,7 +147,9 @@ constexpr static inline std::array< Entry, 8 > configurationVariables = { // make_entry< &Configuration::prompt >("prompt", "1"), make_entry< &Configuration::enablePasswdEmail >("enablePasswdEmail", "true"), make_entry< &Configuration::autopushPrompt >("autopushPrompt", "false"), - make_entry< &Configuration::failMode >("failMode", "deny")}; + make_entry< &Configuration::failMode >("failMode", "deny"), + make_entry< &Configuration::nonInteractiveMode >("nonInteractiveMode", "false") +}; class ConfigurationFactory { public: diff --git a/PAM/ssh/include/rublon/core_handler.hpp b/PAM/ssh/include/rublon/core_handler.hpp index e0a030b..0168318 100755 --- a/PAM/ssh/include/rublon/core_handler.hpp +++ b/PAM/ssh/include/rublon/core_handler.hpp @@ -1,5 +1,6 @@ #pragma once +#include "rublon/error.hpp" #include "rublon/static_string.hpp" #include #include @@ -57,11 +58,13 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { } bool hasException(const Document & coreResponse) const { + log(LogLevel::Trace, "Checking error status in core response"); using namespace std::string_view_literals; return coreResponse.HasMember("status") and coreResponse["status"].GetString() == "ERROR"sv; } bool isUnHealthy(const Document & coreResponse) const { + log(LogLevel::Trace, "Checking errors in core response"); return coreResponse.HasParseError(); } @@ -102,7 +105,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { tl::expected< Document, Error > resp{&alloc}; resp->Parse(response.body.c_str()); - log(LogLevel::Debug, "Begin, Core Response validation"); + log(LogLevel::Debug, "Starting Core Response validation"); if(isUnHealthy(*resp)) { log(LogLevel::Error, "Rublon Core responded with broken data"); @@ -122,11 +125,13 @@ 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 dyring check application step if(auto error = RublonCheckApplicationException::fromString(exceptionString); error.has_value()) return tl::unexpected{Error{error.value()}}; @@ -138,9 +143,10 @@ 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 + // other exceptions, just "throw" - // TODO Handle return tl::unexpected{Error{CoreHandlerError{CoreHandlerError::RublonCoreException}}}; } diff --git a/PAM/ssh/include/rublon/core_handler_interface.hpp b/PAM/ssh/include/rublon/core_handler_interface.hpp index a0974d4..b0ba392 100644 --- a/PAM/ssh/include/rublon/core_handler_interface.hpp +++ b/PAM/ssh/include/rublon/core_handler_interface.hpp @@ -16,7 +16,7 @@ class CoreHandlerInterface { } bool createWSConnection(std::string_view tid) const { - rublon::log(LogLevel::Debug, "%s", "CoreHandlerInterface::listen"); + rublon::log(LogLevel::Debug, "%s", "CoreHandlerInterface::createWSConnection"); return static_cast< const Impl * >(this)->createWSConnection(tid); } diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index 4129d76..162962c 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -62,7 +62,6 @@ class CURL { std::pmr::string response_data{&stackResource}; response_data.reserve(3000); - /// TODO this can be done on stack using pmr auto curl_headers = std::unique_ptr< curl_slist, void (*)(curl_slist *) >(nullptr, curl_slist_free_all); std::for_each(request.headers.begin(), request.headers.end(), [&](auto header) { log(LogLevel::Debug, "%s header: %s: %s", "CURL", header.first.c_str(), header.second.c_str()); diff --git a/PAM/ssh/include/rublon/error.hpp b/PAM/ssh/include/rublon/error.hpp index bbb5d3d..21af816 100755 --- a/PAM/ssh/include/rublon/error.hpp +++ b/PAM/ssh/include/rublon/error.hpp @@ -1,17 +1,14 @@ #pragma once +#include #include #include #include +#include namespace rublon { -template < typename... Types > -constexpr std::array< std::string_view, sizeof...(Types) > make_array(Types... names) { - return {std::forward< Types >(names)...}; -} - class ConfigurationError { public: enum class ErrorClass { // @@ -20,7 +17,7 @@ class ConfigurationError { BadInput, Empty }; - constexpr static auto errorClassPrettyName = make_array( // + constexpr static auto errorClassPrettyName = make_array< std::string_view >( // "RequiredValueNotFound", "BadFailMode", "BadInput", @@ -43,7 +40,7 @@ class ConnectionError { HttpError, ClientError }; - constexpr static auto errorClassPrettyName = make_array( // + constexpr static auto errorClassPrettyName = make_array< std::string_view >( // "Timeout", "Error", "Client Error"); @@ -72,7 +69,7 @@ class CoreHandlerError { TransactionAccessTokenExpiredException, // code: 11 TooManyRequestsException }; - constexpr static auto errorClassPrettyName = make_array( // + constexpr static auto errorClassPrettyName = make_array< std::string_view >( // "BadSigature", "RublonCoreException", "BrokenData", @@ -107,7 +104,7 @@ class CoreHandlerError { class MethodError { public: enum ErrorClass { BadMethod, BadUserInput, NoMethodAvailable }; - constexpr static auto errorClassPrettyName = make_array("BadMethod", "BadUserInput", "NoMethodAvailable"); + constexpr static auto errorClassPrettyName = make_array< std::string_view >("BadMethod", "BadUserInput", "NoMethodAvailable"); constexpr static auto prettyName = "Method Error"; constexpr MethodError(ErrorClass e = BadMethod) : errorClass{e} {} @@ -127,7 +124,7 @@ class WerificationError { PasscodeException, // code: 18 TooManyRequestsException // code: 101 }; - constexpr static auto errorClassPrettyName = make_array( // + constexpr static auto errorClassPrettyName = make_array< std::string_view >( // "BadInput", "SecurityKeyException", "PasscodeException", @@ -162,7 +159,7 @@ class RublonAuthenticationInterrupt { UserWaiting, UserNotFound }; - constexpr static auto errorClassPrettyName = make_array( // + constexpr static auto errorClassPrettyName = make_array< std::string_view >( // "UserBypassedException", "UserDenied", "UserPending", @@ -197,7 +194,7 @@ class RublonCheckApplicationException { UnsupportedVersionException }; - constexpr static auto errorClassPrettyName = make_array( // + constexpr static auto errorClassPrettyName = make_array< std::string_view >( // "MissingFieldException", "ApplicationNotFoundException", "InvalidSignatureException", diff --git a/PAM/ssh/include/rublon/init.hpp b/PAM/ssh/include/rublon/init.hpp index 53e6b67..df8026b 100644 --- a/PAM/ssh/include/rublon/init.hpp +++ b/PAM/ssh/include/rublon/init.hpp @@ -1,6 +1,7 @@ #pragma once #include "rublon/memory.hpp" +#include "rublon/utils.hpp" #include #include @@ -23,16 +24,19 @@ class Init : public AuthenticationStep { const char * apiPath = "/api/transaction/init"; tl::expected< MethodSelect_t, Error > createMethod(const Document & coreResponse) const { - memory::MonotonicStackResource< 256 > stackResource; + memory::MonotonicStackResource< 512 > stackResource; RapidJSONPMRAlloc alloc{&stackResource}; - const auto & rublonResponse = coreResponse["result"]; - - // const auto * rublonMethods = JSONPointer{"/result/methods", &alloc}.Get(coreResponse); + const auto * rublonMethods = JSONPointer{"/result/methods", &alloc}.Get(coreResponse); const auto * rublonTid = JSONPointer{"/result/tid", &alloc}.Get(coreResponse); - _session.updateTransactionId(rublonTid); - return MethodSelect_t{_session, rublonResponse["methods"], _session.config().prompt, _session.config().autopushPrompt}; + if(not rublonMethods) + log(LogLevel::Error, "core response has no methods"); + if(not rublonTid) + log(LogLevel::Error, "core response has no transaction ID"); + + _session.updateTransactionId(rublonTid); + return MethodSelect_t{_session, *rublonMethods, _session.config().prompt, _session.config().autopushPrompt}; } void addPamInfo(Document & coreRequest, const Pam_t & pam) const { @@ -41,39 +45,45 @@ class Init : public AuthenticationStep { } void addParams(Document & coreRequest, const Pam_t & pam) const { - memory::MonotonicStackResource< 512 > stackResource; + memory::MonotonicStackResource< 1024 > stackResource; std::pmr::string releaseInfo{&stackResource}; auto & alloc = coreRequest.GetAllocator(); const auto os = details::osName(&stackResource); + const auto host = details::hostname(&stackResource); if(os == "unknown") { log(LogLevel::Warning, "No OS information available"); } - Value osNamePretty{os.data(), static_cast< unsigned >(os.size()), alloc}; Value ip{pam.ip().get(), alloc}; Value params{rapidjson::kObjectType}; - params.AddMember("userIP", ip, alloc); params.AddMember("appVer", RUBLON_VERSION_STRING, alloc); - params.AddMember("os", osNamePretty, alloc); + if(not host.empty()) + params.AddMember("hostName", Value{os.c_str(), static_cast< unsigned >(host.size()), alloc}, alloc); + if(not _session.inInteractiveMode()) + params.AddMember("mode", "noninteractive", alloc); + params.AddMember("os", Value{os.c_str(), static_cast< unsigned >(os.size()), alloc}, alloc); + params.AddMember("userIP", Value{pam.ip().get(), alloc}, alloc); coreRequest.AddMember("params", std::move(params), alloc); } tl::expected< std::reference_wrapper< const Document >, Error > checkEnrolement(const Document & coreResponse, const Pam_t pam) const { using namespace std::string_view_literals; - const auto & resp = coreResponse; + memory::MonotonicStackResource< 256 > stackResource; + RapidJSONPMRAlloc alloc{&stackResource}; - /// TODO refactor this - if(resp.HasMember("result") and resp["result"].IsObject() and resp["result"].HasMember("status")) { - const auto & status = resp["result"]["status"].GetString(); + const auto * rublonStatus = JSONPointer{"/result/status", &alloc}.Get(coreResponse); + const auto * rublonWebURI = JSONPointer{"/result/webURI", &alloc}.Get(coreResponse); + + if(rublonStatus) { + const auto & status = rublonStatus->GetString(); log(LogLevel::Warning, "Got enrolement message with stats %s", status); if(status == "pending"sv) { - if(resp["result"].HasMember("webURI")) { - const auto & weburi = resp["result"]["webURI"].GetString(); - pam.print("Visit %s", weburi); + if(rublonWebURI) { + pam.print("Visit %s", rublonWebURI->GetString()); } } else if(status == "waiting"sv) { return tl::unexpected{Error{RublonAuthenticationInterrupt{RublonAuthenticationInterrupt::UserWaiting}}}; diff --git a/PAM/ssh/include/rublon/json.hpp b/PAM/ssh/include/rublon/json.hpp index 2a06fa1..d158600 100644 --- a/PAM/ssh/include/rublon/json.hpp +++ b/PAM/ssh/include/rublon/json.hpp @@ -1,5 +1,6 @@ #pragma once +#include "rapidjson/encodings.h" #include #include #include @@ -137,7 +138,6 @@ inline auto begin(const rublon::Value & __ils) noexcept { inline ::rublon::Value::ConstValueIterator end(const rublon::Value & __ils) noexcept { return __ils.End(); } - [[nodiscard]] inline std::size_t size(const rublon::Value & __cont) { return __cont.Size(); } diff --git a/PAM/ssh/include/rublon/method/OTP.hpp b/PAM/ssh/include/rublon/method/OTP.hpp index 365c0ed..045e5f1 100644 --- a/PAM/ssh/include/rublon/method/OTP.hpp +++ b/PAM/ssh/include/rublon/method/OTP.hpp @@ -13,7 +13,6 @@ class OTP : public PasscodeBasedAuth { public: OTP(Session & session, int prompts) : PasscodeBasedAuth(session, - "", "Mobile Passcode", "Enter the passcode from the Authenticator app: ", 6, diff --git a/PAM/ssh/include/rublon/method/PUSH.hpp b/PAM/ssh/include/rublon/method/PUSH.hpp index ba93e22..af1cad1 100644 --- a/PAM/ssh/include/rublon/method/PUSH.hpp +++ b/PAM/ssh/include/rublon/method/PUSH.hpp @@ -1,6 +1,5 @@ #pragma once -#include "rublon/session.hpp" #include #include diff --git a/PAM/ssh/include/rublon/method/SMS.hpp b/PAM/ssh/include/rublon/method/SMS.hpp index e628aab..ddf7f8d 100644 --- a/PAM/ssh/include/rublon/method/SMS.hpp +++ b/PAM/ssh/include/rublon/method/SMS.hpp @@ -12,7 +12,7 @@ namespace rublon::method { class SMS : public PasscodeBasedAuth { public: SMS(Session & session, int prompts) - : PasscodeBasedAuth(session, "", "SMS", "Enter SMS passcode: ", 6, true, PasscodeBasedAuth::Endpoint::ConfirmCode, prompts) {} + : PasscodeBasedAuth(session, "SMS", "Enter SMS passcode: ", 6, true, PasscodeBasedAuth::Endpoint::ConfirmCode, prompts) {} }; } // namespace rublon::method diff --git a/PAM/ssh/include/rublon/method/YOTP.hpp b/PAM/ssh/include/rublon/method/YOTP.hpp index 3ad8192..2140c05 100644 --- a/PAM/ssh/include/rublon/method/YOTP.hpp +++ b/PAM/ssh/include/rublon/method/YOTP.hpp @@ -11,9 +11,8 @@ namespace rublon::method { class YOTP : public PasscodeBasedAuth { public: - YOTP(Session & session, std::string accessToken, int prompts) + YOTP(Session & session, int prompts) : PasscodeBasedAuth(session, - std::move(accessToken), "YubiKey OTP Security Key", "Insert and tap your YubiKey: ", 44, diff --git a/PAM/ssh/include/rublon/method/method_select.hpp b/PAM/ssh/include/rublon/method/method_select.hpp index abe43de..616f821 100644 --- a/PAM/ssh/include/rublon/method/method_select.hpp +++ b/PAM/ssh/include/rublon/method/method_select.hpp @@ -1,11 +1,16 @@ #pragma once -#include "rublon/session.hpp" +#include +#include +#include #include #include #include +#include #include #include +#include +#include #include #include @@ -18,6 +23,9 @@ namespace rublon { +enum class MethodIds { OTP, SMS, PUSH, EMAIL, SmsLink, YOTP, PhoneCall }; +constexpr auto MethodNames = make_array< std::string_view >("totp", "sms", "push", "email", "smsLink", "yotp", "phoneCall"); + class MethodProxy { public: template < typename Method_t > @@ -43,49 +51,63 @@ class PostMethod : public AuthenticationStep { using base_t = AuthenticationStep; const char * uri = "/api/transaction/methodSSH"; - std::string _method; + MethodIds _method; int _prompts; bool _autopushPrompt; - tl::expected< MethodProxy, Error > createMethod(const Document & coreResponse) const { - /// TODO vericodeLength - const auto & rublonResponse = coreResponse["result"]; - // [[deprecated]] std::string tid = rublonResponse["tid"].GetString(); + tl::expected< void, Error > readAccessToken(const Document & coreResponse) const { + log(LogLevel::Debug, "Readding access token"); + auto alloc = RapidJSONPMRStackAlloc< 256 >{}; + const auto * rublonAccessToken = JSONPointer{"/result/token", &alloc}.Get(coreResponse); + if(rublonAccessToken) + _session.updateAccessToken(rublonAccessToken); + return {}; + } - if(_method == "totp") { - return MethodProxy{method::OTP{_session, _prompts}}; - } else if(_method == "sms") { - return MethodProxy{method::SMS{_session, _prompts}}; - } else if(_method == "push") { - return MethodProxy{method::PUSH{_session, _autopushPrompt}}; - } else if(_method == "email") { - return MethodProxy{method::EMAIL{_session}}; - } else if(_method == "smsLink") { - return MethodProxy{method::SmsLink{_session}}; - } else if(_method == "yotp") { - /// TODO assert has token - std::string token = rublonResponse.HasMember("token") ? rublonResponse["token"].GetString() : ""; - return MethodProxy{method::YOTP{_session, std::move(token), _prompts}}; - } else if(_method == "phoneCall") { - return MethodProxy{method::PhoneCall{_session}}; - } else - return tl::unexpected{MethodError{MethodError::BadMethod}}; + tl::expected< MethodProxy, Error > createMethod() const { + log(LogLevel::Debug, "Creating method"); + switch(_method) { + case rublon::MethodIds::OTP: + return MethodProxy{method::OTP{_session, _prompts}}; + + case rublon::MethodIds::SMS: + return MethodProxy{method::SMS{_session, _prompts}}; + + case rublon::MethodIds::PUSH: + return MethodProxy{method::PUSH{_session, _autopushPrompt}}; + + case rublon::MethodIds::EMAIL: + return MethodProxy{method::EMAIL{_session}}; + + case rublon::MethodIds::SmsLink: + return MethodProxy{method::SmsLink{_session}}; + + case rublon::MethodIds::YOTP: + return MethodProxy{method::YOTP{_session, _prompts}}; + + case rublon::MethodIds::PhoneCall: + return MethodProxy{method::PhoneCall{_session}}; + + default: + return tl::unexpected{MethodError{MethodError::BadMethod}}; + } } void addParams(Document & body) const { auto & alloc = body.GetAllocator(); - body.AddMember("method", Value{_method.c_str(), alloc}, alloc); + body.AddMember("method", Value{MethodNames[static_cast< int >(_method)].data(), alloc}, alloc); } public: const char * _name = "Confirm Method"; - PostMethod(Session & session, std::string method, int prompts, bool autopushPrompt) + PostMethod(Session & session, MethodIds method, int prompts, bool autopushPrompt) : base_t(session), _method{method}, _prompts{prompts}, _autopushPrompt{autopushPrompt} {} template < typename Hander_t > tl::expected< MethodProxy, Error > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const { - auto createMethod = [&](const auto & coreResponse) { return this->createMethod(coreResponse); }; + auto readAccessToken = [&](const auto & coreResponse) { return this->readAccessToken(coreResponse); }; + auto createMethod = [&]() { return this->createMethod(); }; RapidJSONPMRStackAlloc< 2 * 1024 > alloc{}; Document body{rapidjson::kObjectType, &alloc}; @@ -96,6 +118,7 @@ class PostMethod : public AuthenticationStep { return coreHandler .request(alloc, uri, body) // + .and_then(readAccessToken) .and_then(createMethod); } }; @@ -110,31 +133,29 @@ class MethodSelect { public: template < typename Array_t > - MethodSelect(Session & session, const Array_t & methodsAvailableForUser, int prompts, bool autopushPrompt) + MethodSelect(Session & session, const Array_t & methodsEnabledInCore, int prompts, bool autopushPrompt) : _session{session}, _prompts{prompts}, _autopushPrompt{autopushPrompt} { + rublon::log(LogLevel::Debug, "Checking what methods from core are supported"); using namespace std::string_view_literals; - memory::MonotonicStackResource< 2024 > stackResource; - std::pmr::vector< std::string_view > _methods; - _methodsAvailable.reserve(std::size(methodsAvailableForUser)); - - std::pmr::set< std::string_view > methodsSupported{ - {"totp"sv, "email"sv, "yotp"sv, "sms"sv, "push"sv, "smsLink"sv, "phoneCall"sv}, &stackResource}; + _methodsAvailable.reserve(std::size(methodsEnabledInCore)); transform_if( - std::begin(methodsAvailableForUser), - std::end(methodsAvailableForUser), + std::begin(methodsEnabledInCore), + std::end(methodsEnabledInCore), std::back_inserter(_methodsAvailable), [&](const auto & method) { return method.GetString(); }, - [&](const auto & method) { return methodsSupported.find(method.GetString()) != methodsSupported.end(); }); + [&](const auto & method) { return std::find(MethodNames.begin(), MethodNames.end(), method.GetString()) != MethodNames.end(); }); rublon::log(LogLevel::Debug, "User has %d methods available", _methodsAvailable.size()); } tl::expected< PostMethod, Error > create(Pam_t & pam) const { rublon::log(LogLevel::Debug, "prompting user to select method"); - memory::StrictMonotonic_4k_HeapResource memoryResource; - std::pmr::map< int, std::string > methods_id{&memoryResource}; /// TODO pmr + memory::StrictMonotonic_2k_HeapResource memoryResource; + + std::pmr::map< int, MethodIds > methods_id{&memoryResource}; std::pmr::map< int, std::pmr::string > methods_names{&memoryResource}; + int prompts = _prompts; if(_methodsAvailable.size() == 0) { @@ -147,65 +168,95 @@ class MethodSelect { auto logMethodAvailable = [](auto & method) { // rublon::log(LogLevel::Debug, "Method %s found", method.c_str()); }; + auto logMethodSkippedInteractive = [](auto & method) { + rublon::log(LogLevel::Debug, "Method %s found, but skipped due non interactive mode", method.c_str()); + }; auto logMethodNotAvailable = [](auto & method) { rublon::log(LogLevel::Debug, "Method %s found, but has no handler", method.c_str()); }; auto printAvailableMethods = [&]() -> tl::expected< int, MethodError > { + rublon::log(LogLevel::Trace, "Printing available methods"); int i{}; for(const auto & method : _methodsAvailable) { if(method == "totp") { + // skipped in non interactive + if(not _session.inInteractiveMode()) { + logMethodSkippedInteractive(method); + continue; + } logMethodAvailable(method); - pam.print("%d: Passcode", i + 1); - methods_id[++i] = method; + if(_session.inInteractiveMode()) + pam.print("%d: Passcode", i + 1); + methods_id[++i] = MethodIds::OTP; methods_names[i] = "Passcode"; continue; } if(method == "email") { + // available in non interactive logMethodAvailable(method); - pam.print("%d: Email Link", i + 1); - methods_id[++i] = method; + if(_session.inInteractiveMode()) + pam.print("%d: Email Link", i + 1); + methods_id[++i] = MethodIds::EMAIL; methods_names[i] = "Email Link"; continue; } if(method == "yotp") { + // skipped in non interactive + if(not _session.inInteractiveMode()) { + logMethodSkippedInteractive(method); + continue; + } logMethodAvailable(method); - pam.print("%d: YubiKey OTP Security Key", i + 1); - methods_id[++i] = method; + if(_session.inInteractiveMode()) + pam.print("%d: YubiKey OTP Security Key", i + 1); + methods_id[++i] = MethodIds::YOTP; methods_names[i] = "YubiKey OTP Security Key"; continue; } if(method == "sms") { + // skipped in non interactive + if(not _session.inInteractiveMode()) { + logMethodSkippedInteractive(method); + continue; + } logMethodAvailable(method); - pam.print("%d: SMS Passcode", i + 1); - methods_id[++i] = method; + if(_session.inInteractiveMode()) + pam.print("%d: SMS Passcode", i + 1); + methods_id[++i] = MethodIds::SMS; methods_names[i] = "SMS Passcode"; continue; } if(method == "push") { + // available in non interactive logMethodAvailable(method); - pam.print("%d: Mobile Push", i + 1); - methods_id[++i] = method; + if(_session.inInteractiveMode()) + pam.print("%d: Mobile Push", i + 1); + methods_id[++i] = MethodIds::PUSH; methods_names[i] = "Mobile Push"; continue; } if(method == "smsLink") { + // available in non interactive logMethodAvailable(method); - pam.print("%d: SMS Link", i + 1); - methods_id[++i] = method; + if(_session.inInteractiveMode()) + pam.print("%d: SMS Link", i + 1); + methods_id[++i] = MethodIds::SmsLink; methods_names[i] = "SMS Link"; continue; } if(method == "phoneCall") { + // available in non interactive logMethodAvailable(method); - pam.print("%d: Phone Call", i + 1); - methods_id[++i] = method; + if(_session.inInteractiveMode()) + pam.print("%d: Phone Call", i + 1); + methods_id[++i] = MethodIds::PhoneCall; methods_names[i] = "Phone Call"; continue; } @@ -225,8 +276,8 @@ class MethodSelect { }; const auto createMethod = [&](std::uint32_t methodid) -> tl::expected< PostMethod, MethodError > { + rublon::log(LogLevel::Trace, "Create method %d", methods_id); auto hasMethod = methods_id.find(methodid) != methods_id.end(); - // pam.print("\t selected: %s", hasMethod ? methods_id.at(methodid).c_str() : "unknown option"); if(!hasMethod) { log(LogLevel::Error, "User selected option %d, which is not correct", methodid); return tl::unexpected{MethodError(MethodError::BadMethod)}; @@ -237,14 +288,36 @@ class MethodSelect { }; const auto askForMethod = [&](int methods_number) -> tl::expected< uint32_t, MethodError > { - if(methods_number == 1) { - pam.print("Automatically selected the only available authentication method: %s", methods_id.at(1).c_str()); - return 1; + if(not _session.inInteractiveMode()) { + log(LogLevel::Debug, "Non interactive method selection"); + /// TODO refactor, this is ugly + auto nonInteractiveMethodsPriority = + make_array< MethodIds >(MethodIds::PUSH, MethodIds::EMAIL, MethodIds::SmsLink, MethodIds::PhoneCall); + + for(const auto methodid : nonInteractiveMethodsPriority) { + for(auto [key, id] : methods_id) { + if(id == methodid) { + log(LogLevel::Debug, + "Automatically selected authentication method: %s due to working in noninteractive mode", + methods_names.at(key).c_str()); + + pam.print("Non-interctive mode enabled, choose method: %s", methods_names.at(key).c_str()); + return key; + } + } + } + return tl::unexpected{MethodError(MethodError::BadMethod)}; + } else { + if(methods_number == 1) { + pam.print("Automatically selected the only available authentication method: %s", methods_names.at(1).c_str()); + return 1; + } + return pam.scan(conv::to_uint32, "\nSelect method [1-%d]: ", methods_number).transform_error(toMethodError); } - return pam.scan(conv::to_uint32, "\nSelect method [1-%d]: ", methods_number).transform_error(toMethodError); }; auto reducePromptCount = [&](int selected_method) -> tl::expected< uint32_t, MethodError > { + rublon::log(LogLevel::Trace, "User has %d prompts available", prompts); prompts--; return selected_method; }; diff --git a/PAM/ssh/include/rublon/method/passcode_based_auth.hpp b/PAM/ssh/include/rublon/method/passcode_based_auth.hpp index 322ebec..1ba5dc9 100644 --- a/PAM/ssh/include/rublon/method/passcode_based_auth.hpp +++ b/PAM/ssh/include/rublon/method/passcode_based_auth.hpp @@ -58,6 +58,7 @@ class PasscodeBasedAuth : public AuthenticationStep { } tl::expected< std::reference_wrapper< Document >, Error > readPasscode(Document & body, const Pam_t & pam) const { + ///TODO assert in interactive mode auto & alloc = body.GetAllocator(); auto vericode = pam.scan([](const char * userInput) { return std::string{userInput}; }, userMessage); @@ -65,8 +66,8 @@ class PasscodeBasedAuth : public AuthenticationStep { Value confirmFieldValue(confirmField, alloc); body.AddMember(confirmFieldValue, Value{vericode.c_str(), alloc}, alloc); - if(token.size()) { - this->addAccessToken(body, token); + if(_session.hasAccessToken()) { + this->addAccessToken(body, _session.accessToken()); } return body; } @@ -79,18 +80,6 @@ class PasscodeBasedAuth : public AuthenticationStep { return AuthenticationStatus{AuthenticationStatus::Action::Confirmed}; } - template < typename Hander_t > - tl::expected< AuthenticationStatus, Error > waitForCoreConfirmation(const CoreHandlerInterface< Hander_t > & eventListener) const { - log(LogLevel::Info, "Listening to confirmation event in PasscodeBasedAuth"); - - auto event = eventListener.listen(); - if(event.status == transactionConfirmed ){ - log(LogLevel::Debug, " transaction confirmed jupi"); - return AuthenticationStatus{AuthenticationStatus::Action::Confirmed, std::string{event.accessToken.value().c_str()}}; - } - log(LogLevel::Debug, " transaction denied"); - return AuthenticationStatus{AuthenticationStatus::Action::Denied}; - } tl::expected< AuthenticationStatus, Error > errorHandler(Error error, const Pam_t & pam, int promptLeft) const { if(promptLeft && error.is< WerificationError >()) { @@ -114,13 +103,11 @@ class PasscodeBasedAuth : public AuthenticationStep { public: const char * _name; - std::string token; // TODO StaticString enum class Endpoint { ConfirmCode, SecurityKeySSH }; PasscodeBasedAuth( // Session & session, - std::string token, const char * _name, const char * userMessage, @@ -135,8 +122,7 @@ class PasscodeBasedAuth : public AuthenticationStep { vericodeLength{length}, onlyDigits{numbersOnly}, _prompts{prompts}, - _name{_name}, - token{std::move(token)} {} + _name{_name} {} template < typename Hander_t > tl::expected< AuthenticationStatus, Error > verify(const CoreHandlerInterface< Hander_t > & coreHandler, const Pam_t & pam) const { diff --git a/PAM/ssh/include/rublon/method/websocket_based_auth.hpp b/PAM/ssh/include/rublon/method/websocket_based_auth.hpp index 75e8c53..0625deb 100644 --- a/PAM/ssh/include/rublon/method/websocket_based_auth.hpp +++ b/PAM/ssh/include/rublon/method/websocket_based_auth.hpp @@ -6,37 +6,39 @@ #include #include #include - #include namespace rublon::method { class WebsocketBasedAuth : public AuthenticationStep { + tl::expected< void, Error > prompt(const Pam_t & pam) const { + if(not _session.inInteractiveMode()) { + return {}; + } + + if(not _autopushPrompt) + pam.print("Autopush"); + else + pam.scan([](const auto /*ignored userinput*/) { return ""; }, + "Rublon authentication initiated. Complete the authentication and press Enter to proceed"); + + return {}; + } + public: const char * _name = ""; const bool _autopushPrompt = true; WebsocketBasedAuth(Session & session, const char * name, bool autopushPrompt = true) : AuthenticationStep(session), _name{name}, _autopushPrompt{autopushPrompt} {} - - /// TODO refactor this code + template < typename Hander_t > tl::expected< AuthenticationStatus, Error > verify(const CoreHandlerInterface< Hander_t > & coreHandler, const Pam_t & pam) const { - log(LogLevel::Info, "starting WS"); - if(not _autopushPrompt) - pam.print("Autopush"); - else - pam.scan([](const auto /*ignored userinput*/) { return ""; }, - "Rublon authentication initiated. Complete the authentication and press Enter to proceed"); - - RublonEventData event = coreHandler.listen(); - - if(event.status == transactionConfirmed ){ - log(LogLevel::Debug, " transaction confirmed jupi"); - return AuthenticationStatus{AuthenticationStatus::Action::Confirmed, std::string{event.accessToken.value().c_str()}}; - } - log(LogLevel::Debug, " transaction denied"); - return AuthenticationStatus{AuthenticationStatus::Action::Denied}; + const auto promprUser = [&]() { return prompt(pam); }; + const auto waitForCoreToConfirm = [&]() { return waitForCoreConfirmation(coreHandler); }; + + return promprUser() // + .and_then(waitForCoreToConfirm); } }; diff --git a/PAM/ssh/include/rublon/pam.hpp b/PAM/ssh/include/rublon/pam.hpp index 9c9770e..5aaa0c1 100644 --- a/PAM/ssh/include/rublon/pam.hpp +++ b/PAM/ssh/include/rublon/pam.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -20,7 +21,7 @@ class LinuxPam { const void * ip = NULL; pam_get_item(pamh, PAM_RHOST, &ip); if(ip == NULL) { - rublon::log(rublon::LogLevel::Warning, "Cant read user from linux PAM"); + rublon::log(rublon::LogLevel::Warning, "Cant read ip from linux PAM"); ip = ""; } return ( const char * ) ip; @@ -40,8 +41,6 @@ class LinuxPam { void print(const char * fmt, Ti... ti) const noexcept { char buf[256] = {}; sprintf(buf, fmt, std::forward< Ti >(ti)...); - log(LogLevel::Debug, "pam_print: '%s'", buf); - if(auto r = pam_prompt(pamh, PAM_TEXT_INFO, nullptr, fmt, std::forward< Ti >(ti)...); r != PAM_SUCCESS) { log(LogLevel::Error, "pam_print returned with error code %d", r); } diff --git a/PAM/ssh/include/rublon/session.hpp b/PAM/ssh/include/rublon/session.hpp index e93d54f..bf22e19 100644 --- a/PAM/ssh/include/rublon/session.hpp +++ b/PAM/ssh/include/rublon/session.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace rublon { @@ -14,20 +15,22 @@ class Session { const 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} { + Session(const Pam_t & pam, const Configuration & config) + : _pam{pam}, _config{config}, _coreHandler{_config} { log(LogLevel::Debug, __PRETTY_FUNCTION__); } Session(Session &&) noexcept = default; Session(const Session &) = delete; - Session& operator=(Session &&) noexcept = default; - Session& operator=(const Session &) = delete; + Session & operator=(Session &&) noexcept = delete; + Session & operator=(const Session &) = delete; const auto & coreHandler() const { return _coreHandler; @@ -46,13 +49,27 @@ class Session { return systemToken().data(); } - /// TODO validate tid + bool inInteractiveMode() const { + return _config.nonInteractiveMode == false; + } + void updateTransactionId(const Value * tid) { - if(tid == nullptr) { - log(LogLevel::Warning, "Transaction ID is not defined"); - } else { - _tid = tid->GetString(); - } + log(LogLevel::Debug, "Set transaction ID %s", tid->GetString()); + _tid = tid->GetString(); + } + + void updateAccessToken(const Value * accessToken) { + log(LogLevel::Debug, "AccessToken %s", accessToken->GetString()); + _accessToken = accessToken->GetString(); + } + bool hasAccessToken() const { + return not _accessToken.empty(); + } + std::string_view accessToken() const { + return _accessToken; + } + const char * caccessToken() const { + return _accessToken.c_str(); } std::string_view transactionID() const { diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index 3721b4a..9078dd4 100755 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -33,7 +34,7 @@ inline bool readFile(const std::filesystem::path & path, T & destination) { return true; } -enum class LogLevel { Debug, Info, Warning, Error }; +enum class LogLevel { Trace, Debug, Info, Warning, Error }; inline StaticString< 32 > dateStr() { StaticString< 32 > date; @@ -43,45 +44,12 @@ inline StaticString< 32 > dateStr() { return date; } -constexpr const char * LogLevelNames[]{"Debug", "Info", "Warning", "Error"}; +constexpr const char * LogLevelNames[]{"Trace", "Debug", "Info", "Warning", "Error"}; LogLevel g_level = LogLevel::Debug; constexpr bool syncLogFile = true; static const char * application = ""; namespace details { - std::pmr::string osName(std::pmr::memory_resource * mr) { - memory::MonotonicStackResource< 8 * 1024 > stackResource; - - std::ifstream file(std::filesystem::path{"/etc/os-release"}); - if(not file.good()) - return {"unknown", mr}; - - std::pmr::string line{&stackResource}; - line.reserve(100); - - while(std::getline(file, line)) { - std::pmr::string _key{&stackResource}; - std::pmr::string _value{&stackResource}; - - if(!line.length()) - continue; - - if(line[0] == '#' || line[0] == ';') - continue; - - auto posEqual = line.find('='); - _key = line.substr(0, posEqual); - _value = line.substr(posEqual + 1); - - if(_key == "PRETTY_NAME") { - _value.erase(std::remove_if(_value.begin(), _value.end(), [](auto ch) { return ch == '"'; }), _value.end()); - return {_value, mr}; - } - } - - return {"unknown", mr}; - } - constexpr const char * logPath() { constexpr auto path = "/var/log/rublon-ssh.log"; return path; @@ -228,6 +196,49 @@ namespace details { } } + std::pmr::string hostname(std::pmr::memory_resource * mr) { + std::pmr::string hostname{512, '\0', mr}; + if(gethostname(hostname.data(), hostname.size()) != 0) { + log(LogLevel::Warning, "Hostname is not available"); + return ""; + } + hostname.resize(hostname.find_first_of('\0')); + return hostname; + } + + std::pmr::string osName(std::pmr::memory_resource * mr) { + memory::MonotonicStackResource< 8 * 1024 > stackResource; + + std::ifstream file(std::filesystem::path{"/etc/os-release"}); + if(not file.good()) + return {"unknown", mr}; + + std::pmr::string line{&stackResource}; + line.reserve(100); + + while(std::getline(file, line)) { + std::pmr::string _key{&stackResource}; + std::pmr::string _value{&stackResource}; + + if(!line.length()) + continue; + + if(line[0] == '#' || line[0] == ';') + continue; + + auto posEqual = line.find('='); + _key = line.substr(0, posEqual); + _value = line.substr(posEqual + 1); + + if(_key == "PRETTY_NAME") { + _value.erase(std::remove_if(_value.begin(), _value.end(), [](auto ch) { return ch == '"'; }), _value.end()); + return {_value, mr}; + } + } + + return {"unknown", mr}; + } + } // namespace details template < class InputIterator, class OutputIterator, class UnaryOperator, class Pred > @@ -250,4 +261,9 @@ struct ci_less { } }; +template < typename Out, typename... Types > +constexpr std::array< Out, sizeof...(Types) > make_array(Types... names) { + return {std::forward< Types >(names)...}; +} + } // namespace rublon diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index f20ac1f..e37c877 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -201,8 +201,10 @@ class WebSocket { if(input.substr(0, 2) == "42") { /// TODO assert _this /// TODO assert currentEvent + /// TODO refactor to separate class if(_this->currentEvent == nullptr) return -1; + log(LogLevel::Debug, "WS got %s", input.data()); size_t startPos = input.find("[\"") + 2; size_t endPos = input.find("\",", startPos); auto & event = *_this->currentEvent; @@ -222,7 +224,8 @@ class WebSocket { Document dataJson{&alloc}; dataJson.Parse(jsonString.data(), jsonString.size()); - const auto * data = JSONPointer{"/data", &alloc}.Get(dataJson); + const auto * data = JSONPointer{"/data", &alloc}.Get(dataJson); + const auto * redirectUrl = JSONPointer{"/redirectUrl", &alloc}.Get(dataJson); if(data != nullptr) { Document tokenJson{&alloc}; @@ -232,10 +235,11 @@ class WebSocket { if(token != nullptr) { event.accessToken = token->GetString(); } else { - log(LogLevel::Debug, "got broken data"); + log(LogLevel::Error, "Response does not contain token"); } - - log(LogLevel::Debug, "mark event received"); + _this->event_received = true; + } else if(redirectUrl != nullptr) { + log(LogLevel::Info, "Received deny message"); _this->event_received = true; } else { log(LogLevel::Error, "event data incorrect"); diff --git a/PAM/ssh/lib/pam.cpp b/PAM/ssh/lib/pam.cpp index 0f8d1cd..ee3b197 100644 --- a/PAM/ssh/lib/pam.cpp +++ b/PAM/ssh/lib/pam.cpp @@ -33,18 +33,23 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu using namespace rublon; details::initLog(); LinuxPam pam{pamh}; + + log(LogLevel::Info, "user '%s' authentication attempt", pam.username().get()); auto printAuthMessageAndExit = [&](const AuthenticationStatus status) { switch(status.action()) { case AuthenticationStatus::Action::Bypass: + log(LogLevel::Info, "user '%s' authentication BYPASSED", pam.username().get()); pam.print("RUBLON authentication BYPASSED"); return PAM_SUCCESS; case AuthenticationStatus::Action::Denied: + log(LogLevel::Info, "user '%s' authentication FAILED", pam.username().get()); pam.print("RUBLON authentication FAILED"); return PAM_MAXTRIES; case AuthenticationStatus::Action::Confirmed: + log(LogLevel::Info, "user '%s' authentication SUCCEEDED", pam.username().get()); pam.print("RUBLON authentication SUCCEEDED"); return PAM_SUCCESS; } @@ -95,8 +100,8 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu { CheckApplication ca; - auto ret = - ca.call(CH, {session.value().config().systemToken.data(), session.value().config().systemToken.size()}).or_else(mapError); + const auto & config = session.value().config(); + const auto ret = ca.call(CH, {config.systemToken.data(), config.systemToken.size()}).or_else(mapError); if(not ret.has_value()) { log(LogLevel::Error, "Check Application step failed, check configration"); return PAM_MAXTRIES; diff --git a/PAM/ssh/patches/rapidjson.patch b/PAM/ssh/patches/rapidjson.patch new file mode 100644 index 0000000..e43d863 --- /dev/null +++ b/PAM/ssh/patches/rapidjson.patch @@ -0,0 +1,51 @@ +diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h +index e3e20dfb..592c5678 100644 +--- a/include/rapidjson/document.h ++++ b/include/rapidjson/document.h +@@ -97,17 +97,20 @@ struct GenericMember { + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + template + class GenericMemberIterator + : public std::iterator >::Type> { +- ++ + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef std::iterator BaseType; ++#pragma GCC diagnostic pop + + public: + //! Iterator type itself +@@ -1936,7 +1939,10 @@ private: + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wclass-memaccess" + std::memcpy(e, values, count * sizeof(GenericValue)); ++#pragma GCC diagnostic pop + } + else + SetElementsPointer(0); +@@ -1949,7 +1955,10 @@ private: + if (count) { + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wclass-memaccess" + std::memcpy(m, members, count * sizeof(Member)); ++#pragma GCC diagnostic pop + } + else + SetMembersPointer(0); +diff --git a/package.json b/package.json +index 843463d7..cc6087a5 100644 +Binary files a/package.json and b/package.json differ diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..7f16ccb --- /dev/null +++ b/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +source_dir=$1 +build_dir=$2 +tag=#3 + +cmake --build $build_dir -- -j +cmake --build $build_dir --target package -- -j + +cp $build_dir/*rpm $source_dir/_tmp/packages 2>/dev/null || cp $build_dir/*deb $source_dir/_tmp/packages 2>/dev/null + diff --git a/build_all.py b/build_all.py new file mode 100644 index 0000000..dd1d09d --- /dev/null +++ b/build_all.py @@ -0,0 +1,136 @@ +import os +import subprocess +from concurrent.futures import ThreadPoolExecutor, as_completed +from pydoc import doc + +# Configuration +ROOT_DIR = "./os" +MAX_WORKERS = 8 # Maximum parallel jobs for build docker image and configure project +MAX_WORKERS_COMPILE = 2 # number of compilation jobs t run + +def find_dockerfiles(root_dir): + """Recursively find all Dockerfiles in the given directory.""" + dockerfiles = [] + for dirpath, _, filenames in os.walk(root_dir): + for filename in filenames: + if filename == "Dockerfile": + dockerfiles.append(os.path.join(dirpath, filename)) + return dockerfiles + + +def tags_from_path(dockerfile): + dir_path = os.path.dirname(dockerfile) + tag = dir_path.replace(ROOT_DIR + "/", "").replace(os.path.sep, "_") + return dir_path, tag + + +def build_docker_image(dockerfile): + """Build a Docker image for the given Dockerfile.""" + dir_path, tag = tags_from_path(dockerfile) + print(f"Starting build image step for {tag} (directory: {dir_path})...") + try: + log_file = f"_tmp/logs/{tag}/build_docker_image.log" + os.makedirs(os.path.dirname(log_file), exist_ok=True) + with open(log_file, "w") as log: + result = subprocess.run( + ["docker", "build", "-t", tag, dir_path], + stdout=log, + stderr=subprocess.STDOUT, + check=True + ) + return f"Successfully built {tag}" + except subprocess.CalledProcessError: + print(f"Failed to build {tag}. Check {log_file} for details.") + return f"Failed to build {tag}" + + +in_image_source_dir = "/home/rublon-ssh" +in_image_build_dir = "/home/build" + +def configure_rublon(dockerfile): + """Build a Docker image for the given Dockerfile.""" + dir_path, tag = tags_from_path(dockerfile) + print(f"Starting configure step for {tag} (directory: {dir_path})...") + try: + log_file = f"_tmp/logs/{tag}/configure_rublon_image.log" + build_dir = f"_tmp/builds/{tag}" + os.makedirs(os.path.dirname(log_file), exist_ok=True) + os.makedirs(os.path.dirname(build_dir), exist_ok=True) + with open(log_file, "w") as log: + result = subprocess.run( + # $tag /bin/bash /home/rublon-ssh/configure.sh $tag + ["docker", "run", + "-v", f"./:{in_image_source_dir}", + "-v", f"./{build_dir}:{in_image_build_dir}", + tag, + f"{in_image_source_dir}/configure.sh", in_image_source_dir, in_image_build_dir + ], + stdout=log, + stderr=subprocess.STDOUT, + check=True + ) + return f"Successfully built {tag}" + except subprocess.CalledProcessError: + print(f"Failed to build {tag}. Check {log_file} for details.") + return f"Failed to build {tag}" + + +def build_rublon(dockerfile): + """Build a Docker image for the given Dockerfile.""" + dir_path, tag = tags_from_path(dockerfile) + print(f"Starting build for {tag} (directory: {dir_path})...") + try: + log_file = f"_tmp/logs/{tag}/compile_rublon_image.log" + build_dir = f"_tmp/builds/{tag}" + packages_dir = "_tmp/packages/" + os.makedirs(os.path.dirname(log_file), exist_ok=True) + os.makedirs(os.path.dirname(build_dir), exist_ok=True) + os.makedirs(os.path.dirname(packages_dir), exist_ok=True) + with open(log_file, "w") as log: + result = subprocess.run( + # $tag /bin/bash /home/rublon-ssh/configure.sh $tag + ["docker", "run", + "-v", f"./:{in_image_source_dir}", + "-v", f"./{build_dir}:{in_image_build_dir}", + tag, + f"{in_image_source_dir}/build.sh", in_image_source_dir, in_image_build_dir, tag + ], + stdout=log, + stderr=subprocess.STDOUT, + check=True + ) + return f"Successfully built {tag}" + except subprocess.CalledProcessError: + print(f"Failed to build {tag}. Check {log_file} for details.") + return f"Failed to build {tag}" + + +def run_parralel(num, job, name, dockerfiles): + # Use ThreadPoolExecutor for parallel builds + with ThreadPoolExecutor(max_workers=num) as executor: + future_to_dockerfile = {executor.submit(job, df): df for df in dockerfiles} + for future in as_completed(future_to_dockerfile): + dockerfile = future_to_dockerfile[future] + try: + result = future.result() + print(result) + except Exception as e: + print(f"Job {name} failed for {dockerfile}: {e}") + + +def main(): + # Find all Dockerfiles + dockerfiles = find_dockerfiles(ROOT_DIR) + if not dockerfiles: + print("No Dockerfiles found.") + return + + run_parralel(MAX_WORKERS, build_docker_image, "Docker Image Build", dockerfiles) + run_parralel(MAX_WORKERS, configure_rublon, "Configuring project", dockerfiles) + run_parralel(MAX_WORKERS_COMPILE, build_rublon, "Building project", dockerfiles) + + + print("All Docker builds completed.") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/build_all_packages.sh b/build_all_packages.sh deleted file mode 100755 index b664148..0000000 --- a/build_all_packages.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -home_dir=$PWD -echo $home_dir -find . -type f -name Dockerfile | while read -r file_path; do - dir_path=$(dirname "$file_path") - file_name=$(echo $dir_path | cut -d'/' -f3- | sed 's|/|_|g') - docker build -t "$file_name" $dir_path - docker run -v `pwd`:/home/rublon-ssh/ $file_name /bin/bash /home/rublon-ssh/build_project.sh $file_name -done diff --git a/build_package.sh b/build_package.sh deleted file mode 100755 index 7389d19..0000000 --- a/build_package.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -cmake -B rublon_ssh-build /home/rublon-ssh/ -cmake --build rublon_ssh-build/ -cmake --build rublon_ssh-build/ --target package -- -j -if [ ! -d /home/rublon-ssh/$1_packages ]; then - mkdir /home/rublon-ssh/$1_packages -fi -cp rublon_ssh-build/*rpm /home/rublon-ssh/$1_packages 2>/dev/null || cp rublon_ssh-build/*deb /home/rublon-ssh/$1_packages 2>/dev/null - diff --git a/configure.sh b/configure.sh new file mode 100755 index 0000000..e8ee21b --- /dev/null +++ b/configure.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +source_dir=$1 +build_dir=$2 + +rm -Rf $build_dir || true +cmake -B $build_dir -DCMAKE_BUILD_TYPE=Release $source_dir diff --git a/helpers/centos-base.repo b/helpers/centos-base.repo new file mode 100644 index 0000000..e779276 --- /dev/null +++ b/helpers/centos-base.repo @@ -0,0 +1,31 @@ +##################################################################### +# CentOS-Base.repo +# +# The mirror system uses the connecting IP address of the client and the +# update status of each mirror to pick mirrors that are updated to and +# geographically close to the client. You should use this for CentOS updates +# unless you are manually picking other mirrors. +# +# If the mirrorlist= does not work for you, as a fall back you can try the +# remarked out baseurl= line instead. +# +# +[centosApp] +name=CentOSApp-9 +baseurl=https://yum.oracle.com/repo/OracleLinux/OL8/appstream/x86_64/ +enabled=1 +gpgcheck=0 +gpgkey=http://mirror.centos.org/centos/8/os/x86_64/RPM-GPG-KEY-CentOS-8 +[centosApp2] +name=CentOSApp-8 +baseurl=https://yum.oracle.com/repo/OracleLinux/OL8/baseos/latest/x86_64/ +enabled=1 +gpgcheck=0 +gpgkey=http://mirror.centos.org/centos/8/os/x86_64/RPM-GPG-KEY-CentOS-8 +#[centosBase] +#name=CentOSBase-9 +#baseurl=https://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os/ +#enabled=1 +#gpgcheck=0 +#gpgkey=http://mirror.centos.org/centos/8/os/x86_64/RPM-GPG-KEY-CentOS-8 +##################################################################### diff --git a/helpers/centos-base_9.repo b/helpers/centos-base_9.repo new file mode 100644 index 0000000..78d8ad1 --- /dev/null +++ b/helpers/centos-base_9.repo @@ -0,0 +1,31 @@ +##################################################################### +# CentOS-Base.repo +# +# The mirror system uses the connecting IP address of the client and the +# update status of each mirror to pick mirrors that are updated to and +# geographically close to the client. You should use this for CentOS updates +# unless you are manually picking other mirrors. +# +# If the mirrorlist= does not work for you, as a fall back you can try the +# remarked out baseurl= line instead. +# +# +[centosApp] +name=CentOSApp-9 +baseurl=https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/ +enabled=1 +gpgcheck=0 +gpgkey=http://mirror.centos.org/centos/8/os/x86_64/RPM-GPG-KEY-CentOS-8 +[centosApp2] +name=CentOSAppBase-9 +baseurl=https://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os/ +enabled=1 +gpgcheck=0 +gpgkey=http://mirror.centos.org/centos/8/os/x86_64/RPM-GPG-KEY-CentOS-8 +#[centosBase] +#name=CentOSBase-9 +#baseurl=https://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os/ +#enabled=1 +#gpgcheck=0 +#gpgkey=http://mirror.centos.org/centos/8/os/x86_64/RPM-GPG-KEY-CentOS-8 +##################################################################### diff --git a/os/alma/8/Vagrantfile b/os/alma/8/Vagrantfile index e1681b2..f9238c4 100644 --- a/os/alma/8/Vagrantfile +++ b/os/alma/8/Vagrantfile @@ -23,44 +23,36 @@ Vagrant.configure("2") do |config| config.vm.synced_folder "../../..", "/home/vagrant/Rublon-Linux" config.vm.provider "virtualbox" do |vb| - vb.memory = 8024 - - # Display the VirtualBox GUI when booting the machine - vb.gui = false - - # Fix for 'SSH auth method: Private key' stuck - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] + vb.gui = false + vb.memory = 2048 + vb.cpus = 2 + + # Fix for 'SSH auth method: Private key' stuck + vb.customize ["modifyvm", :id, "--cableconnected1", "on"] end # Enable provisioning with a shell script. Additional provisioners such as # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=alma08 - export BUILDDIR=${BASE}/${DISTRO} + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=alma08 + export BUILDDIR=/home/vagrant/build + yum update - yum install -y --nogpgcheck gcc wget openssl-devel openssh-server libcurl-devel pam-devel git cmake policycoreutils-devel checkpolicy gcc-c++ rpm-build libubsan libasan redhat-lsb-core - - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - mkdir socket.io-client-cpp/build_alma; cd socket.io-client-cpp/build_alma - cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release .. - cmake --build . --target install - - #handle semodule - wget https://repo.almalinux.org/almalinux/8/devel/x86_64/os/Packages/rapidjson-devel-1.1.0-3.module_el8.8.0%2B3567%2B56a616e4.noarch.rpm - rpm -i rapidjson* - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build/ -- -j - cmake --build rublon_ssh-build/ --target package -- -j + yum install -y --nogpgcheck gcc gcc-c++ openssl-devel openssh-server libcurl-devel pam-devel cmake policycoreutils-devel checkpolicy rpm-build redhat-lsb-core + + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) + useradd -s /bin/bash -m bwi echo "bwi:bwi"|chpasswd - cd ./rublon_ssh-build - yum install -y rublon*.rpm + yum install -y ${BUILDDIR}/rublon*.rpm SHELL end diff --git a/os/alma/9/Vagrantfile b/os/alma/9/Vagrantfile index 393f2f3..a77d5dc 100644 --- a/os/alma/9/Vagrantfile +++ b/os/alma/9/Vagrantfile @@ -23,41 +23,35 @@ Vagrant.configure("2") do |config| config.vm.synced_folder "../../..", "/home/vagrant/Rublon-Linux" config.vm.provider "virtualbox" do |vb| - vb.memory = 8024 - vb.cpus = 4 - # Display the VirtualBox GUI when booting the machine - vb.gui = false - - # Fix for 'SSH auth method: Private key' stuck - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] + vb.gui = false + vb.memory = 2048 + vb.cpus = 2 + + # Fix for 'SSH auth method: Private key' stuck + vb.customize ["modifyvm", :id, "--cableconnected1", "on"] end # Enable provisioning with a shell script. Additional provisioners such as # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=alma09 - export BUILDDIR=${BASE}/${DISTRO} + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=alma09 + export BUILDDIR=/home/vagrant/build + yum update - yum install -y --nogpgcheck gcc wget openssl-devel openssh-server libcurl-devel pam-devel git cmake policycoreutils-devel checkpolicy gcc-c++ rpm-build* + yum install -y --nogpgcheck gcc gcc-c++ openssl-devel openssh-server libcurl-devel pam-devel cmake policycoreutils-devel checkpolicy rpm-build* cp ${BASE}/helpers/centos-base_9.repo /etc/yum.repos.d/centos-base_9.repo - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - mkdir socket.io-client-cpp/build_alma; cd socket.io-client-cpp/build_alma - cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release .. - cmake --build . --target install + + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) - #handle semodule - wget https://repo.almalinux.org/almalinux/9/devel/x86_64/os/Packages/rapidjson-devel-1.1.0-19.el9.x86_64.rpm - rpm -i ./rapid* - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build/ -- -j - cmake --build rublon_ssh-build/ --target package -- -j - yum install -y ./rublon_ssh-build/rublon* + yum install -y ${BUILDDIR}/rublon* useradd -s /bin/bash -m bwi echo "bwi:bwi"|chpasswd SHELL diff --git a/os/centos/stream9/Dockerfile b/os/centos/stream9/Dockerfile index fb926c8..34aae3c 100644 --- a/os/centos/stream9/Dockerfile +++ b/os/centos/stream9/Dockerfile @@ -1,6 +1,7 @@ FROM carterjones/centos-stream9 -RUN yum update && yum install -y gcc \ +RUN yum -y update && yum install -y \ + gcc gcc-c++ \ openssl-devel \ libcurl-devel \ pam-devel \ @@ -8,8 +9,8 @@ RUN yum update && yum install -y gcc \ policycoreutils-devel \ checkpolicy \ rpm-build \ - openssh \ - gcc-c++ + openssh + WORKDIR /home/Rublon-Linux/ diff --git a/os/centos/stream9/Vagrantfile b/os/centos/stream9/Vagrantfile index b798a60..05894e3 100755 --- a/os/centos/stream9/Vagrantfile +++ b/os/centos/stream9/Vagrantfile @@ -35,27 +35,24 @@ Vagrant.configure("2") do |config| # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=centos09 - export BUILDDIR=${BASE}/${DISTRO} + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=centos09 + export BUILDDIR=/home/vagrant/${DISTRO} + yum update - yum install -y gcc openssl-devel libcurl-devel pam-devel git rapidjson-devel cmake policycoreutils-devel checkpolicy rpm-build openssh + yum install -y gcc gcc-c++ openssl-devel libcurl-devel pam-devel cmake policycoreutils-devel checkpolicy rpm-build openssh - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - mkdir socket.io-client-cpp/build; cd socket.io-client-cpp/build - cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release .. - cmake --build . --target install + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc)y + cmake --build ${BUILDDIR} --target package -j$(nproc) - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build -- -j - cmake --build rublon_ssh-build --target package -- -j useradd -s /bin/bash -m bwi echo "bwi:bwi"|chpasswd - sudo yum install -y ./rublon_ssh-build/rublon*.rpm + sudo yum install -y ${BUILDDIR}/rublon*.rpm SHELL end diff --git a/os/debian/11/Dockerfile b/os/debian/11/Dockerfile index 55a4143..c02ba20 100644 --- a/os/debian/11/Dockerfile +++ b/os/debian/11/Dockerfile @@ -1,14 +1,12 @@ FROM debian:11 ENV DEBIAN_FRONTEND=noniteracactive -RUN apt update && apt install -y gcc \ - g++ \ +RUN apt update && apt install -y \ build-essential \ openssh-server \ libcurl4-openssl-dev \ libpam0g-dev \ libssl-dev \ cmake \ - git \ file WORKDIR /home/Rublon-Linux/ diff --git a/os/debian/11/Vagrantfile b/os/debian/11/Vagrantfile index 8c908f1..63cbc40 100755 --- a/os/debian/11/Vagrantfile +++ b/os/debian/11/Vagrantfile @@ -22,50 +22,43 @@ Vagrant.configure("2") do |config| # argument is a set of non-required options. config.vm.synced_folder "../../..", "/home/vagrant/Rublon-Linux" - config.vm.provider "virtualbox" do |vb| - vb.memory = 4048 - vb.cpus = 4 - # Display the VirtualBox GUI when booting the machine - vb.gui = false - - # Fix for 'SSH auth method: Private key' stuck - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] - end + config.vm.provider "virtualbox" do |vb| + vb.gui = false + vb.memory = 2048 + vb.cpus = 2 + + # Fix for 'SSH auth method: Private key' stuck + vb.customize ["modifyvm", :id, "--cableconnected1", "on"] + end # Enable provisioning with a shell script. Additional provisioners such as # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=debian11 - export BUILDDIR=${BASE}/${DISTRO} - DEBIAN_FRONTEND=noniteracactive\ - apt-get update && apt-get install -y \ - gcc \ - build-essential \ - openssh-server \ - libcurl4-openssl-dev \ - libpam0g-dev \ - libssl-dev \ - git \ - rapidjson-dev \ - cmake + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=debian11 + export BUILDDIR=/home/vagrant/build - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - cmake -B socket.io-build -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release socket.io-client-cpp - cmake --build socket.io-build --target install -- -j + DEBIAN_FRONTEND=noniteracactive \ + apt-get update && apt-get install -y \ + build-essential \ + openssh-server \ + libcurl4-openssl-dev \ + libpam0g-dev \ + libssl-dev \ + cmake - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build -- -j - cmake --build rublon_ssh-build --target package -- -j - # Register Rublon pam - - useradd -s /bin/bash -m bwi - echo "bwi:bwi"|chpasswd - sudo dpkg -i ./rublon_ssh-build/rublon* + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) + + # Register Rublon pam + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd + sudo dpkg -i ${BUILDDIR}/rublon*.deb SHELL end diff --git a/os/debian/12/Dockerfile b/os/debian/12/Dockerfile index a3f5da0..953a68c 100644 --- a/os/debian/12/Dockerfile +++ b/os/debian/12/Dockerfile @@ -1,14 +1,12 @@ FROM debian:12 ENV DEBIAN_FRONTEND=noniteracactive -RUN apt update && apt install -y gcc \ - g++ \ +RUN apt update && apt install -y \ build-essential \ openssh-server \ libcurl4-openssl-dev \ libpam0g-dev \ libssl-dev \ cmake \ - git \ file WORKDIR /home/Rublon-Linux/ diff --git a/os/debian/12/Vagrantfile b/os/debian/12/Vagrantfile index c348cdd..c77380c 100755 --- a/os/debian/12/Vagrantfile +++ b/os/debian/12/Vagrantfile @@ -23,10 +23,9 @@ Vagrant.configure("2") do |config| config.vm.synced_folder "../../..", "/home/vagrant/Rublon-Linux" config.vm.provider "virtualbox" do |vb| - # Display the VirtualBox GUI when booting the machine vb.gui = false - vb.memory = 8044 - vb.cpus = 4 + vb.memory = 2048 + vb.cpus = 2 # Fix for 'SSH auth method: Private key' stuck vb.customize ["modifyvm", :id, "--cableconnected1", "on"] @@ -36,36 +35,30 @@ Vagrant.configure("2") do |config| # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=debian12 - export BUILDDIR=${BASE}/${DISTRO} - DEBIAN_FRONTEND=noniteracactive\ - apt-get update && apt-get install -y \ - gcc \ - build-essential \ - openssh-server \ - libcurl4-openssl-dev \ - libpam0g-dev \ - libssl-dev \ - git \ - rapidjson-dev \ - cmake - - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - cmake -B socket.io-build -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release socket.io-client-cpp - cmake --build socket.io-build --target install -- -j + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=debian12 + export BUILDDIR=/home/vagrant/build - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build -- -j - cmake --build rublon_ssh-build --target package -- -j - # Register Rublon pam - - useradd -s /bin/bash -m bwi - echo "bwi:bwi"|chpasswd - sudo dpkg -i ${BUILDDIR}/rublon_ssh-build/rublon* + DEBIAN_FRONTEND=noniteracactive \ + apt-get update && apt-get install -y \ + build-essential \ + openssh-server \ + libcurl4-openssl-dev \ + libpam0g-dev \ + libssl-dev \ + cmake + + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) + + # Register Rublon pam + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd + sudo dpkg -i ${BUILDDIR}/rublon*.deb SHELL end diff --git a/os/opensuse/15sp3/Vagrantfile b/os/opensuse/15sp3/Vagrantfile index 1f280d2..ca3437b 100644 --- a/os/opensuse/15sp3/Vagrantfile +++ b/os/opensuse/15sp3/Vagrantfile @@ -37,35 +37,29 @@ Vagrant.configure("2") do |config| config.vm.provision "shell", inline: <<-SHELL export BASE=/home/vagrant/Rublon-Linux export DISTRO=opensuse15 - export BUILDDIR=${BASE}/${DISTRO} + export BUILDDIR=/home/vagrant/${DISTRO} zypper --non-interactive install -y \ gcc \ gcc-c++ \ openssh-server \ libcurl-devel \ libpamtest-devel \ - git \ - rapidjson-devel \ cmake \ pam-devel \ openssl-devel \ rpm-build - # get dependencies - mkdir ${BUILDDIR} -p; - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - cmake -B socket.io-build -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release socket.io-client-cpp - cmake --build socket.io-build --target install -- -j - - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build -- -j - cmake --build rublon_ssh-build --target package -- -j - # Register Rublon pam - - useradd -s /bin/bash -m bwi - echo "bwi:bwi"|chpasswd - sudo zypper --no-gpg-checks install -y ${BUILDDIR}/rublon_ssh-build/rublon* + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) + + # Register Rublon pam + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd + sudo zypper --no-gpg-checks install -y ${BUILDDIR}/rublon* SHELL end diff --git a/os/rhel/8/Dockerfile b/os/rhel/8/Dockerfile index 9602bee..14377c8 100644 --- a/os/rhel/8/Dockerfile +++ b/os/rhel/8/Dockerfile @@ -9,7 +9,6 @@ RUN yum update -y && yum install -y gcc \ openssh-server \ gcc-c++ \ wget -RUN wget https://yum.oracle.com/repo/OracleLinux/OL8/baseos/latest/x86_64/getPackage/pam-1.3.1-34.0.1.el8_10.x86_64.rpm \ - https://yum.oracle.com/repo/OracleLinux/OL8/baseos/latest/x86_64/getPackage/pam-devel-1.3.1-34.0.1.el8_10.x86_64.rpm +RUN wget https://yum.oracle.com/repo/OracleLinux/OL8/baseos/latest/x86_64/getPackage/pam-devel-1.3.1-34.0.1.el8_10.x86_64.rpm RUN rpm -Uvh pam* diff --git a/os/rhel/8/Vagrantfile b/os/rhel/8/Vagrantfile index 286d08a..35252e5 100644 --- a/os/rhel/8/Vagrantfile +++ b/os/rhel/8/Vagrantfile @@ -24,39 +24,40 @@ Vagrant.configure("2") do |config| # config.vm.synced_folder "../../../../socket.io", "/home/vagrant/socket.io" config.vm.provider "virtualbox" do |vb| - vb.memory = 8024 - # Display the VirtualBox GUI when booting the machine - vb.gui = false - - # Fix for 'SSH auth method: Private key' stuck - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] + vb.gui = false + vb.memory = 2048 + vb.cpus = 2 + + # Fix for 'SSH auth method: Private key' stuck + vb.customize ["modifyvm", :id, "--cableconnected1", "on"] end # Enable provisioning with a shell script. Additional provisioners such as # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=rhel8 - export BUILDDIR=${BASE}/${DISTRO} - cp ${BASE}/helpers/centos-base.repo /etc/yum.repos.d/ - yum install -y openssl-devel libcurl systemd-pam git rapidjson-devel cmake pam-devel libcurl-devel libasan libubsan rpm-build redhat-lsb-core - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - cmake -B socket.io-build -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release socket.io-client-cpp - cmake --build socket.io-build --target install -- -j - useradd -s /bin/bash -m bwi - echo "bwi:bwi"|chpasswd - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build -- -j - cmake --build rublon_ssh-build --target package -- -j - # Register Rublon pam - yum -y install ./rublon_ssh-build/rublon-ssh*.el8.rpm + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=rhel8 + export BUILDDIR=/home/vagrant/build + + cp ${BASE}/helpers/centos-base.repo /etc/yum.repos.d/ + yum update + yum install -y openssl-devel libcurl systemd-pam cmake pam-devel libcurl-devel rpm-build redhat-lsb-core + + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) - SHELL + # Register Rublon pam + yum -y install ${BUILDDIR}/rublon-ssh*.el8.rpm + + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd + SHELL end diff --git a/os/rhel/9/Vagrantfile b/os/rhel/9/Vagrantfile index 3d80237..ce86f1f 100755 --- a/os/rhel/9/Vagrantfile +++ b/os/rhel/9/Vagrantfile @@ -23,41 +23,38 @@ Vagrant.configure("2") do |config| config.vm.synced_folder "../../..", "/home/vagrant/Rublon-Linux" config.vm.provider "virtualbox" do |vb| - vb.memory = 6024 - - # Display the VirtualBox GUI when booting the machine - vb.gui = false - - # Fix for 'SSH auth method: Private key' stuck - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] + vb.gui = false + vb.memory = 2048 + vb.cpus = 2 + + # Fix for 'SSH auth method: Private key' stuck + vb.customize ["modifyvm", :id, "--cableconnected1", "on"] end # Enable provisioning with a shell script. Additional provisioners such as # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=rhel9 - export BUILDDIR=${BASE}/${DISTRO} - cp ${BASE}/helpers/centos-base_9.repo /etc/yum.repos.d/centos-base_9.repo + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=rhel9 + export BUILDDIR=/home/vagrant/build + + cp ${BASE}/helpers/centos-base_9.repo /etc/yum.repos.d/centos-base_9.repo yum update - yum install -y gcc openssl-devel openssh libcurl systemd-pam git rapidjson-devel cmake rpm-build lsb-release pam-devel libcurl-devel - useradd -s /bin/bash -m bwi - echo "bwi:bwi"|chpasswd - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - cmake -B socket.io-build -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release socket.io-client-cpp - cmake --build socket.io-build --target install -- -j + yum install -y gcc openssl-devel openssh libcurl systemd-pam cmake rpm-build lsb-release pam-devel libcurl-devel - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build -- -j - cmake --build rublon_ssh-build --target package -- -j - # Register Rublon pam - yum -y install ./rublon_ssh-build/rublon*.rpm + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) + # Register Rublon pam + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd + yum -y install ${BUILDDIR}/rublon*.rpm SHELL end diff --git a/os/rocky_linux/Vagrantfile b/os/rocky_linux/Vagrantfile index 3a400bc..9906dcc 100644 --- a/os/rocky_linux/Vagrantfile +++ b/os/rocky_linux/Vagrantfile @@ -23,37 +23,36 @@ Vagrant.configure("2") do |config| # argument is a set of non-required options. config.vm.provider "virtualbox" do |vb| - vb.memory = 8024 - # Display the VirtualBox GUI when booting the machine - vb.gui = false - # Fix for 'SSH auth method: Private key' stuck - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] + vb.gui = false + vb.memory = 2048 + vb.cpus = 2 + + # Fix for 'SSH auth method: Private key' stuck + vb.customize ["modifyvm", :id, "--cableconnected1", "on"] end # Enable provisioning with a shell script. Additional provisioners such as # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=rocky9 - export BUILDDIR=${BASE}/${DISTRO} - yum install -y epel-release - yum install -y gcc openssl-devel systemd-pam git cmake gcc-c++ rapidjson-devel wget pam-devel rpm-build libcurl-devel policycoreutils-python-utils - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - cmake -B socket.io-build -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release socket.io-client-cpp - cmake --build socket.io-build --target install -- -j + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=rocky9 + export BUILDDIR=/home/vagrant/build + + yum install -y epel-release + yum install -y gcc gcc-c++ openssl-devel systemd-pam cmake pam-devel rpm-build libcurl-devel policycoreutils-python-utils - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build -- -j - cmake --build rublon_ssh-build --target package -- -j - useradd -s /bin/bash -m bwi - echo "bwi:bwi"|chpasswd - sudo yum install -y ./rublon_ssh-build/rublon*.rpm + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) + + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd + sudo yum install -y ./rublon_ssh-build/rublon*.rpm SHELL - end diff --git a/os/ubuntu/20.04/Dockerfile b/os/ubuntu/20.04/Dockerfile index 02b64e1..c99d695 100644 --- a/os/ubuntu/20.04/Dockerfile +++ b/os/ubuntu/20.04/Dockerfile @@ -1,7 +1,6 @@ FROM ubuntu:20.04 ENV DEBIAN_FRONTEND=noniteracactive -RUN apt update && apt install -y gcc \ - g++ \ +RUN apt update && apt install -y \ build-essential \ openssh-server \ libcurl4-openssl-dev \ diff --git a/os/ubuntu/20.04/Vagrantfile b/os/ubuntu/20.04/Vagrantfile index 6e80bae..7cf927a 100755 --- a/os/ubuntu/20.04/Vagrantfile +++ b/os/ubuntu/20.04/Vagrantfile @@ -23,50 +23,42 @@ Vagrant.configure("2") do |config| config.vm.synced_folder "../../..", "/home/vagrant/Rublon-Linux" config.vm.provider "virtualbox" do |vb| - vb.memory = 8060 - # Display the VirtualBox GUI when booting the machine - vb.gui = false - - # Fix for 'SSH auth method: Private key' stuck - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] + vb.gui = false + vb.memory = 2048 + vb.cpus = 2 + + # Fix for 'SSH auth method: Private key' stuck + vb.customize ["modifyvm", :id, "--cableconnected1", "on"] end # Enable provisioning with a shell script. Additional provisioners such as # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=ubuntu2004 - export BUILDDIR=${BASE}/${DISTRO} - - DEBIAN_FRONTEND=noniteracactive\ - apt-get update && apt-get install -y \ - gcc \ - g++ \ - build-essential \ - openssh-server \ - libcurl4-openssl-dev \ - libpam0g-dev \ - libssl-dev \ - git \ - rapidjson-dev \ - cmake + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=ubuntu2004 + export BUILDDIR=/home/vagrant/build + + DEBIAN_FRONTEND=noniteracactive \ + apt-get update && apt-get install -y \ + build-essential \ + openssh-server \ + libcurl4-openssl-dev \ + libpam0g-dev \ + libssl-dev \ + cmake - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - cmake -B socket.io-build -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release socket.io-client-cpp - cmake --build socket.io-build --target install -- -j - ln -s /usr/bin/make /usr/bin/gmake - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build -- -j - cmake --build rublon_ssh-build --target package -- -j - # Register Rublon pam - - useradd -s /bin/bash -m bwi - echo "bwi:bwi"|chpasswd - sudo dpkg -i ${BUILDDIR}/rublon_ssh-build/rublon*.deb - SHELL + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) + + # Register Rublon pam + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd + sudo dpkg -i ${BUILDDIR}/rublon*.deb + SHELL end diff --git a/os/ubuntu/22.04/Dockerfile b/os/ubuntu/22.04/Dockerfile index 3ba8733..5f9ddd6 100644 --- a/os/ubuntu/22.04/Dockerfile +++ b/os/ubuntu/22.04/Dockerfile @@ -1,7 +1,6 @@ FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noniteracactive -RUN apt update && apt install -y gcc \ - g++ \ +RUN apt update && apt install -y \ build-essential \ openssh-server \ libcurl4-openssl-dev \ diff --git a/os/ubuntu/22.04/Vagrantfile b/os/ubuntu/22.04/Vagrantfile index 81b63e5..08cf101 100755 --- a/os/ubuntu/22.04/Vagrantfile +++ b/os/ubuntu/22.04/Vagrantfile @@ -23,43 +23,42 @@ Vagrant.configure("2") do |config| config.vm.synced_folder "../../..", "/home/vagrant/Rublon-Linux" config.vm.provider "virtualbox" do |vb| - vb.memory = 4000 - # Display the VirtualBox GUI when booting the machine - vb.gui = false - - # Fix for 'SSH auth method: Private key' stuck - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] + vb.gui = false + vb.memory = 2048 + vb.cpus = 2 + + # Fix for 'SSH auth method: Private key' stuck + vb.customize ["modifyvm", :id, "--cableconnected1", "on"] end # Enable provisioning with a shell script. Additional provisioners such as # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=ubuntu2204 - export BUILDDIR=${BASE}/${DISTRO} - DEBIAN_FRONTEND=noniteracactive\ - apt-get update && apt-get install -y \ - gcc \ - build-essential \ - openssh-server \ - libcurl4-openssl-dev \ - libpam0g-dev \ - libssl-dev \ - git \ - rapidjson-dev \ - libwebsockets-dev \ - cmake + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=ubuntu2204 + export BUILDDIR=/home/vagrant/build - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build -- -j - cmake --build rublon_ssh-build --target package -- -j - # Register Rublon pam - - useradd -s /bin/bash -m bwi - echo "bwi:bwi"|chpasswd - sudo dpkg -i ${BUILDDIR}/rublon_ssh-build/rublon*.deb + DEBIAN_FRONTEND=noniteracactive \ + apt-get update && apt-get install -y \ + build-essential \ + openssh-server \ + libcurl4-openssl-dev \ + libpam0g-dev \ + libssl-dev \ + cmake + + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) + + # Register Rublon pam + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd + sudo dpkg -i ${BUILDDIR}/rublon*.deb SHELL end diff --git a/os/ubuntu/24.04/Dockerfile b/os/ubuntu/24.04/Dockerfile index e61132a..f14efce 100644 --- a/os/ubuntu/24.04/Dockerfile +++ b/os/ubuntu/24.04/Dockerfile @@ -1,7 +1,6 @@ FROM ubuntu:24.04 ENV DEBIAN_FRONTEND=noniteracactive -RUN apt update && apt install -y gcc \ - g++ \ +RUN apt update && apt install -y \ build-essential \ openssh-server \ libcurl4-openssl-dev \ diff --git a/os/ubuntu/24.04/Vagrantfile b/os/ubuntu/24.04/Vagrantfile index cf07b1d..f511c2f 100644 --- a/os/ubuntu/24.04/Vagrantfile +++ b/os/ubuntu/24.04/Vagrantfile @@ -22,48 +22,42 @@ Vagrant.configure("2") do |config| config.vm.synced_folder "../../..", "/home/vagrant/Rublon-Linux" config.vm.provider "virtualbox" do |vb| - vb.memory = 4000 - # Display the VirtualBox GUI when booting the machine - vb.gui = false - - # Fix for 'SSH auth method: Private key' stuck - vb.customize ["modifyvm", :id, "--cableconnected1", "on"] + vb.gui = false + vb.memory = 2048 + vb.cpus = 2 + + # Fix for 'SSH auth method: Private key' stuck + vb.customize ["modifyvm", :id, "--cableconnected1", "on"] end # Enable provisioning with a shell script. Additional provisioners such as # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - export BASE=/home/vagrant/Rublon-Linux - export DISTRO=ubuntu2404 - export BUILDDIR=${BASE}/${DISTRO} - DEBIAN_FRONTEND=noniteracactive\ - apt-get update && apt-get install -y \ - gcc \ - build-essential \ - openssh-server \ - libcurl4-openssl-dev \ - libpam0g-dev \ - libssl-dev \ - git \ - rapidjson-dev \ - cmake + export BASE=/home/vagrant/Rublon-Linux + export DISTRO=ubuntu2404 + export BUILDDIR=/home/vagrant/build - # get dependencies - mkdir ${BUILDDIR} -p; cd ${BUILDDIR} - git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git - cmake -B socket.io-build -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release socket.io-client-cpp - cmake --build socket.io-build --target install -- -j - ln -s /usr/bin/make /usr/bin/gmake - # Build project - cd ${BUILDDIR} - cmake -B rublon_ssh-build .. - cmake --build rublon_ssh-build -- -j - cmake --build rublon_ssh-build --target package -- -j - # Register Rublon pam - - useradd -s /bin/bash -m bwi - echo "bwi:bwi"|chpasswd - sudo dpkg -i ${BUILDDIR}/rublon_ssh-build/rublon*.deb + DEBIAN_FRONTEND=noniteracactive \ + apt-get update && apt-get install -y \ + build-essential \ + openssh-server \ + libcurl4-openssl-dev \ + libpam0g-dev \ + libssl-dev \ + cmake + + # remove old build if exists + rm ${BUILDDIR} -Rf || true + + # build package + cmake -B ${BUILDDIR} ${BASE} + cmake --build ${BUILDDIR} -j$(nproc) + cmake --build ${BUILDDIR} --target package -j$(nproc) + + # Register Rublon pam + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd + sudo dpkg -i ${BUILDDIR}/rublon*.deb SHELL end diff --git a/pack.cmake b/pack.cmake index 977c398..bf8fefa 100644 --- a/pack.cmake +++ b/pack.cmake @@ -29,7 +29,12 @@ if ( ${outOS} MATCHES "ubuntu" OR ${outOS} MATCHES "debian" ) set(CPACK_DEBIAN_PAM-DEV_PACKAGE_NAME, YES) set(CPACK_DEB_COMPONENT_INSTALL YES) set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS YES) - +set(CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/service/login_rublon.mod" ) + list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/service/login_rublon.pp") + list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/service/login_rublon.te") + list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/service/pam_service.txt") + list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/service/rublon_veritas") + set(CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/service/inst_pubkey_rhel9.sh") execute_process ( COMMAND bash -c "awk -F= '/^VERSION_ID=/{print $2}' /etc/os-release |tr -d '\n' | tr -d '\"'" OUTPUT_VARIABLE VERSION_ID diff --git a/service/01-rublon-ssh_pubkey.conf.default b/service/01-rublon-ssh_pubkey.conf.default index 780de4c..ab5a371 100644 --- a/service/01-rublon-ssh_pubkey.conf.default +++ b/service/01-rublon-ssh_pubkey.conf.default @@ -1,6 +1,6 @@ UsePAM yes -ChallengeResponseAuthentication yes LoginGraceTime 15m +ChallengeResponseAuthentication yes AuthenticationMethods publickey,keyboard-interactive MaxAuthTries 3 PubkeyAuthentication yes diff --git a/service/helpers/postinst_pubkey b/service/helpers/postinst_pubkey deleted file mode 100644 index 2442758..0000000 --- a/service/helpers/postinst_pubkey +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -SSHD_CONF=/etc/ssh/sshd_config -SSHD_PAM_CONF=/etc/pam.d/sshd -RUBLON_SSH_CONFIG=/etc/ssh/sshd_config.d/01-rublon-ssh.conf - -cp -a /usr/share/rublon/01-rublon-ssh_pubkey.conf.default $RUBLON_SSH_CONFIG -chown root:root $RUBLON_SSH_CONFIG -chmod 640 $RUBLON_SSH_CONFIG - -if [ -f /etc/os-release ] -then - . /etc/os-release -fi -grep -qe 'auth requisite pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth requisite pam_rublon.so' $SSHD_PAM_CONF - -if [ ${OS} == "Ubuntu"] -then - grep -qe 'account required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aaccount required pam_rublon.so' $SSHD_PAM_CONF -fi - -grep -qe '@include common-auth' $SSHD_PAM_CONF || sed -i 's/@include common-auth/#@include common-auth/' $SSHD_PAM_CONF - -systemctrl restart sshd - diff --git a/service/helpers/postrm_rhel b/service/helpers/postrm_rhel index 53153ee..bca698b 100644 --- a/service/helpers/postrm_rhel +++ b/service/helpers/postrm_rhel @@ -3,6 +3,7 @@ RUBLON_CONFIG=/etc/rublon.config RUBLON_SSH_CONFIG=/etc/ssh/sshd_config.d/01-rublon-ssh.conf SSHD_PAM_CONF=/etc/pam.d/sshd +VERITAS_PATH=/usr/openv/netbackup/sec/at/bin/ if [ -f $RUBLON_CONFIG ] then diff --git a/service/helpers/pubkey_install_deb b/service/helpers/pubkey_install_deb deleted file mode 100644 index c4d59fc..0000000 --- a/service/helpers/pubkey_install_deb +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -SSHD_CONF=/etc/ssh/sshd_config -SSHD_PAM_CONF=/etc/pam.d/sshd -RUBLON_SSH_CONFIG=/etc/ssh/sshd_config.d/01-rublon-ssh.conf - -cp -a /usr/share/rublon/01-rublon-ssh_pubkey.conf.default $RUBLON_SSH_CONFIG -chown root:root $RUBLON_SSH_CONFIG -chmod 640 $RUBLON_SSH_CONFIG - -if [ -f /etc/os-release ] -then - . /etc/os-release -fi -sed -i '/auth required pam_rublon.so/d' $SSHD_PAM_CONF - -if [[ $ID == "rhel" || $ID=="alma" || $ID=="rocky" || $ID=="Centos" ]] -then -grep -qe 'auth required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth required pam_rublon.so' $SSHD_PAM_CONF -grep -qe '#auth substack password-auth' $SSHD_PAM_CONF || sed -i -e 's/auth substack password-auth/#auth substack password-auth/g' $SSHD_PAM_CONF - -elif [ $ID = "Debian" ] -then -grep -qe 'auth requisite pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth requisite pam_rublon.so' $SSHD_PAM_CONF -else - grep -qe 'auth requisite pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth requisite pam_rublon.so' $SSHD_PAM_CONF - grep -qe 'account required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aaccount required pam_rublon.so' $SSHD_PAM_CONF -fi - -grep -qe '#@include common-auth' $SSHD_PAM_CONF || sed -i 's/@include common-auth/#@include common-auth/' $SSHD_PAM_CONF -if [[ $ID == "rhel" || $ID=="alma" || $ID=="rocky" || $ID=="Centos" ]] -then -systemctl restart sshd -else -deb-systemd-invoke restart ssh.service -fi - diff --git a/service/inst_pubkey.sh b/service/inst_pubkey.sh new file mode 100644 index 0000000..4daa5da --- /dev/null +++ b/service/inst_pubkey.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +SSHD_CONF=/etc/ssh/sshd_config +SSHD_PAM_CONF=/etc/pam.d/sshd +RUBLON_SSH_CONFIG=/etc/ssh/sshd_config.d/01-rublon-ssh.conf + +cp -a /usr/share/rublon/01-rublon-ssh_pubkey.conf.default $RUBLON_SSH_CONFIG +chown root:root $RUBLON_SSH_CONFIG +chmod 640 $RUBLON_SSH_CONFIG + +grep -qe 'auth required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth required pam_rublon.so' $SSHD_PAM_CONF +grep -qe 'account required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aaccount required pam_rublon.so' $SSHD_PAM_CONF +grep -qe '#@include \+common-auth' $SSHD_PAM_CONF || sed -i 's/@include \+common-auth/#@include common-auth/' $SSHD_PAM_CONF + +deb-systemd-invoke restart ssh.service + + diff --git a/service/inst_pubkey_rhel_8.sh b/service/inst_pubkey_rhel_8.sh new file mode 100644 index 0000000..07a392b --- /dev/null +++ b/service/inst_pubkey_rhel_8.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +SSHD_CONF=/etc/ssh/sshd_config +SSHD_PAM_CONF=/etc/pam.d/sshd +RUBLON_SSH_CONFIG=/etc/ssh/01-rublon-ssh.conf + +cp -a /usr/share/rublon/01-rublon-ssh_pubkey.conf.default $RUBLON_SSH_CONFIG +chown root:root $RUBLON_SSH_CONFIG +chmod 640 $RUBLON_SSH_CONFIG + +grep -qe 'account required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aaccount required pam_rublon.so' $SSHD_PAM_CONF +grep -qe '#auth \+substack \+password-auth' $SSHD_PAM_CONF || sed -i 's/auth \+substack \+password-auth/#auth substack password-auth/' $SSHD_PAM_CONF + +systemctl restart sshd + diff --git a/service/inst_pubkey_rhel_9.sh b/service/inst_pubkey_rhel_9.sh new file mode 100644 index 0000000..3feffde --- /dev/null +++ b/service/inst_pubkey_rhel_9.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +SSHD_CONF=/etc/ssh/sshd_config +SSHD_PAM_CONF=/etc/pam.d/sshd +RUBLON_SSH_CONFIG=/etc/ssh/sshd_config.d/01-rublon-ssh.conf + +cp -a /usr/share/rublon/01-rublon-ssh_pubkey.conf.default $RUBLON_SSH_CONFIG +chown root:root $RUBLON_SSH_CONFIG +chmod 640 $RUBLON_SSH_CONFIG + +grep -qe 'auth required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth required pam_rublon.so' $SSHD_PAM_CONF +grep -qe 'account required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aaccount required pam_rublon.so' $SSHD_PAM_CONF +grep -qe '#auth \+substack \+password-auth' $SSHD_PAM_CONF || sed -i 's/auth \+substack \+password-auth/#auth substack password-auth/' $SSHD_PAM_CONF + +systemctl restart sshd + + diff --git a/service/postinst_pubkey b/service/postinst_pubkey deleted file mode 100644 index 1acc74b..0000000 --- a/service/postinst_pubkey +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -SSHD_CONF=/etc/ssh/sshd_config -SSHD_PAM_CONF=/etc/pam.d/sshd -RUBLON_CONFIG=/etc/rublon.config -RUBLON_SSH_CONFIG=/etc/ssh/sshd_config.d/01-rublon-ssh.conf - -if [ ! -f $RUBLON_CONFIG ] -then - cp -a /usr/share/rublon/rublon.config.defaults $RUBLON_CONFIG - chown root:root $RUBLON_CONFIG - chmod 640 $RUBLON_CONFIG -fi -cp -a /usr/share/rublon/service/01-rublon-ssh_pubkey.conf.default $RUBLON_SSH_CONFIG -chown root:root $RUBLON_SSH_CONFIG -chmod 640 $RUBLON_SSH_CONFIG - -if [ -f /etc/os-release ] -then - . /etc/os-release -fi - -#if [[ $ID == "rhel" || $ID=="alma" || $ID=="rocky" ]] -#then -# cd /usr/share/rublon/service - # checkmodule -M -m -o login_rublon.mod login_rublon.te -# semodule_package -o login_rublon.pp -m login_rublon.mod -# semodule -i login_rublon.pp -#fi - -grep -qe 'auth required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth required pam_rublon.so' $SSHD_PAM_CONF -grep -qe 'account required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aaccount required pam_rublon.so' $SSHD_PAM_CONF -grep -qe '@include common-auth' $SSHD_PAM_CONF || sed -i 's/@include common-auth/#@include common-auth/' $SSHD_PAM_CONF - -if [[ $ID == "rhel" || $ID=="alma" || $ID=="rocky" ]] -then - systemctl restart sshd -else - deb-systemd-invoke restart ssh.service -fi - diff --git a/service/pubkey_install_deb b/service/pubkey_install_deb deleted file mode 100644 index c4d59fc..0000000 --- a/service/pubkey_install_deb +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -SSHD_CONF=/etc/ssh/sshd_config -SSHD_PAM_CONF=/etc/pam.d/sshd -RUBLON_SSH_CONFIG=/etc/ssh/sshd_config.d/01-rublon-ssh.conf - -cp -a /usr/share/rublon/01-rublon-ssh_pubkey.conf.default $RUBLON_SSH_CONFIG -chown root:root $RUBLON_SSH_CONFIG -chmod 640 $RUBLON_SSH_CONFIG - -if [ -f /etc/os-release ] -then - . /etc/os-release -fi -sed -i '/auth required pam_rublon.so/d' $SSHD_PAM_CONF - -if [[ $ID == "rhel" || $ID=="alma" || $ID=="rocky" || $ID=="Centos" ]] -then -grep -qe 'auth required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth required pam_rublon.so' $SSHD_PAM_CONF -grep -qe '#auth substack password-auth' $SSHD_PAM_CONF || sed -i -e 's/auth substack password-auth/#auth substack password-auth/g' $SSHD_PAM_CONF - -elif [ $ID = "Debian" ] -then -grep -qe 'auth requisite pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth requisite pam_rublon.so' $SSHD_PAM_CONF -else - grep -qe 'auth requisite pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aauth requisite pam_rublon.so' $SSHD_PAM_CONF - grep -qe 'account required pam_rublon.so' $SSHD_PAM_CONF || sed -i '$aaccount required pam_rublon.so' $SSHD_PAM_CONF -fi - -grep -qe '#@include common-auth' $SSHD_PAM_CONF || sed -i 's/@include common-auth/#@include common-auth/' $SSHD_PAM_CONF -if [[ $ID == "rhel" || $ID=="alma" || $ID=="rocky" || $ID=="Centos" ]] -then -systemctl restart sshd -else -deb-systemd-invoke restart ssh.service -fi - -- 2.45.2 From cd65d742a5ae800440f480b685544b97010e5287 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 30 May 2025 10:05:02 +0200 Subject: [PATCH 02/37] Add base PROXY support implementation --- PAM/ssh/include/rublon/check_application.hpp | 9 +++- PAM/ssh/include/rublon/configuration.hpp | 22 +++++++-- PAM/ssh/include/rublon/core_handler.hpp | 2 +- PAM/ssh/include/rublon/curl.hpp | 49 ++++++++++++++++++-- PAM/ssh/include/rublon/memory.hpp | 11 +++++ PAM/ssh/include/rublon/static_string.hpp | 6 +++ PAM/ssh/include/rublon/websockets.hpp | 34 +++++++++++++- 7 files changed, 121 insertions(+), 12 deletions(-) diff --git a/PAM/ssh/include/rublon/check_application.hpp b/PAM/ssh/include/rublon/check_application.hpp index c038c75..b84a595 100644 --- a/PAM/ssh/include/rublon/check_application.hpp +++ b/PAM/ssh/include/rublon/check_application.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -13,10 +14,16 @@ namespace rublon { +struct closefile_deleter { + void operator()(FILE * f) const { + pclose(f); + } +}; + std::string exec(const char * cmd) { std::array< char, 128 > buffer; std::string result; - std::unique_ptr< FILE, decltype(&pclose) > pipe(popen(cmd, "r"), pclose); + std::unique_ptr< FILE, closefile_deleter > pipe(popen(cmd, "r")); if(!pipe) { return ""; } diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 6e8ae4a..5c335d0 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -28,6 +28,14 @@ class Configuration { bool autopushPrompt{}; FailMode failMode{}; bool nonInteractiveMode{}; + + StaticString< 8 > proxyType{}; + StaticString< 4096 > proxyServer{}; + StaticString< 256 > proxyUsername{}; + StaticString< 256 > proxyPass{}; + int proxyPort{}; + bool proxyAuthRequired{}; + bool proxyEnabled{}; }; namespace { @@ -139,7 +147,7 @@ constexpr auto make_entry(const char * name, const char * defaultValue) { return Entry{name, defaultValue, Entry::make_read_function< member >()}; } -constexpr static inline std::array< Entry, 9 > configurationVariables = { // +constexpr static inline std::array< Entry, 16 > configurationVariables = { make_entry< &Configuration::logging >("logging", "true"), make_entry< &Configuration::systemToken >("systemToken", nullptr), make_entry< &Configuration::secretKey >("secretKey", nullptr), @@ -148,7 +156,15 @@ constexpr static inline std::array< Entry, 9 > configurationVariables = { // make_entry< &Configuration::enablePasswdEmail >("enablePasswdEmail", "true"), make_entry< &Configuration::autopushPrompt >("autopushPrompt", "false"), make_entry< &Configuration::failMode >("failMode", "deny"), - make_entry< &Configuration::nonInteractiveMode >("nonInteractiveMode", "false") + make_entry< &Configuration::nonInteractiveMode >("nonInteractiveMode", "false"), + + make_entry< &Configuration::proxyType >("proxyType", nullptr), + make_entry< &Configuration::proxyServer >("proxyServer", nullptr), + make_entry< &Configuration::proxyUsername >("proxyUsername", nullptr), + make_entry< &Configuration::proxyPass >("proxyPass", nullptr), + make_entry< &Configuration::proxyPort >("proxyPort", "8080"), + make_entry< &Configuration::proxyAuthRequired >("proxyAuthRequired", "false"), + make_entry< &Configuration::proxyEnabled >("proxyEnabled", "false") // }; class ConfigurationFactory { @@ -156,7 +172,7 @@ class ConfigurationFactory { ConfigurationFactory() = default; std::optional< Configuration > systemConfig() { - memory::MonotonicStackResource< 8 * 1024 > stackResource; + memory::MonotonicStackResource< 16 * 1024 > stackResource; Configuration configuration{}; std::ifstream file(std::filesystem::path{"/etc/rublon.config"}); diff --git a/PAM/ssh/include/rublon/core_handler.hpp b/PAM/ssh/include/rublon/core_handler.hpp index 0168318..1e88c78 100755 --- a/PAM/ssh/include/rublon/core_handler.hpp +++ b/PAM/ssh/include/rublon/core_handler.hpp @@ -73,7 +73,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { public: CoreHandler() = delete; - CoreHandler(const Configuration & config) : _config{config}, _ws{std::make_unique(_config.apiServer)}, http{} { + CoreHandler(const Configuration & config) : _config{config}, _ws{std::make_unique(_config.apiServer)}, http{_config} { log(LogLevel::Debug, "Core Handler apiServer: %s", _config.apiServer.c_str()); } CoreHandler(CoreHandler &&) noexcept = default; diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index 162962c..912aa91 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -1,7 +1,9 @@ #pragma once +#include "rublon/memory.hpp" #include #include +#include #include @@ -51,22 +53,59 @@ struct Response { class CURL { std::unique_ptr< ::CURL, void (*)(::CURL *) > curl; - + const Configuration &_config; public: - CURL() : curl{std::unique_ptr< ::CURL, void (*)(::CURL *) >(curl_easy_init(), curl_easy_cleanup)} {} + CURL(const Configuration &config) : curl{std::unique_ptr< ::CURL, void (*)(::CURL *) >(curl_easy_init(), curl_easy_cleanup)}, _config{config} {} tl::expected< std::reference_wrapper< Response >, ConnectionError > request(std::string_view uri, const Request & request, Response & response) const { - memory::MonotonicStackResource< 4 * 1024 > stackResource; + using namespace memory::literals; + memory::Monotonic_16k_HeapResource memoryResource; - std::pmr::string response_data{&stackResource}; - response_data.reserve(3000); + std::pmr::string response_data{&memoryResource}; + response_data.reserve(4_kB); auto curl_headers = std::unique_ptr< curl_slist, void (*)(curl_slist *) >(nullptr, curl_slist_free_all); std::for_each(request.headers.begin(), request.headers.end(), [&](auto header) { log(LogLevel::Debug, "%s header: %s: %s", "CURL", header.first.c_str(), header.second.c_str()); curl_headers.reset(curl_slist_append(curl_headers.release(), (header.first + ": " + header.second).c_str())); }); + + // Optional: Build full proxy URL if proxy is enabled + if (_config.proxyEnabled) { + std::pmr::string proxyUrl{&memoryResource}; + proxyUrl.reserve(4_kB); + + if (_config.proxyType == "http" || _config.proxyType == "https" || _config.proxyType == "socks4" || _config.proxyType == "socks5") { + proxyUrl = _config.proxyType.c_str(); + proxyUrl += "://"; + proxyUrl += _config.proxyServer.c_str(); + if (_config.proxyPort > 0) { + proxyUrl += ":"; + proxyUrl += std::to_string(_config.proxyPort); + } + + curl_easy_setopt(curl.get(), CURLOPT_PROXY, proxyUrl.c_str()); + + if (_config.proxyType == "socks4") { + curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); + } else if (_config.proxyType == "socks5") { + curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); + } else { + curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } + + if (_config.proxyAuthRequired) { + std::pmr::string proxyAuth{&memoryResource}; + proxyAuth.reserve(1_kB); + _config.proxyUsername.c_str(); + proxyAuth += ":"; + proxyAuth += _config.proxyPass.c_str(); + + curl_easy_setopt(curl.get(), CURLOPT_PROXYUSERPWD, proxyAuth.c_str()); + } + } + } curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 0); curl_easy_setopt(curl.get(), CURLOPT_URL, uri.data()); diff --git a/PAM/ssh/include/rublon/memory.hpp b/PAM/ssh/include/rublon/memory.hpp index 5a8c878..d6a9e7b 100644 --- a/PAM/ssh/include/rublon/memory.hpp +++ b/PAM/ssh/include/rublon/memory.hpp @@ -4,6 +4,13 @@ namespace rublon { namespace memory { + + namespace literals{ + constexpr std::uint64_t operator"" _kB(unsigned long long kilobytes) { + return kilobytes * 1024ULL; + } + } + struct default_memory_resource { static inline std::pmr::memory_resource * _mr = std::pmr::get_default_resource(); }; @@ -58,11 +65,15 @@ namespace memory { 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 >; } // namespace memory // class RublonMemory { diff --git a/PAM/ssh/include/rublon/static_string.hpp b/PAM/ssh/include/rublon/static_string.hpp index c4f5298..5d0ac45 100644 --- a/PAM/ssh/include/rublon/static_string.hpp +++ b/PAM/ssh/include/rublon/static_string.hpp @@ -89,4 +89,10 @@ class StaticString : public details::StaticStringBase { private: std::array< char, N + 1 > m_str{}; }; + +template +bool operator==(const StaticString &lhs, const char* rhs){ + return strcmp(lhs.c_str(), rhs) == 0; +} + } // namespace rublon diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index e37c877..5abc469 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -1,5 +1,6 @@ #pragma once +#include "rublon/configuration.hpp" #include "rublon/json.hpp" #include "rublon/memory.hpp" #include "rublon/static_string.hpp" @@ -7,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +30,7 @@ struct RublonEventData { }; class WebSocket { - std::string url; /// TODO pmr + std::reference_wrapper< const Configuration > _config; std::string_view urlv; bool event_received = false; @@ -42,7 +44,11 @@ class WebSocket { RublonEventData * currentEvent{nullptr}; public: - WebSocket(std::string_view uri) : url{uri.data()}, urlv{url} { + WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer.c_str(), _config.get().apiServer.size()} { + using namespace memory::literals; + memory::Monotonic_8k_HeapResource memoryResource; + const auto & cfg = _config.get(); + auto lws_log_emit = [](int level, const char * line) { LogLevel rlevel{}; if(level == LLL_ERR) @@ -70,6 +76,30 @@ class WebSocket { context = lws_create_context(&info); + if(cfg.proxyEnabled && (cfg.proxyType == "http" || cfg.proxyType == "https")) { + std::pmr::string proxyUrl{&memoryResource}; + proxyUrl.reserve(4_kB); + + proxyUrl += cfg.proxyType.data(); + proxyUrl += "://"; + + if(cfg.proxyAuthRequired) { + proxyUrl += cfg.proxyUsername.c_str(); + proxyUrl += ":"; + proxyUrl += cfg.proxyPass.c_str(); + proxyUrl += "@"; + } + + proxyUrl += cfg.proxyServer.c_str(); + if(cfg.proxyPort > 0) { + proxyUrl += ":"; + proxyUrl += std::to_string(cfg.proxyPort); + } + + // Set environment variable for libwebsockets to pick up + setenv((cfg.proxyType == "https" ? "https_proxy" : "http_proxy"), proxyUrl.c_str(), 1); + } + const std::string_view prefix = "https://"; if(urlv.substr(0, prefix.size()) == prefix) -- 2.45.2 From 8edd7684889bef5840ff686031ab913af5592dbd Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 30 May 2025 11:21:27 +0200 Subject: [PATCH 03/37] Remove some dynamic memory allocations --- PAM/ssh/CMakeLists.txt | 2 +- .../rublon/authentication_step_interface.hpp | 2 +- PAM/ssh/include/rublon/core_handler.hpp | 6 ++-- PAM/ssh/include/rublon/init.hpp | 2 -- PAM/ssh/include/rublon/pam_action.hpp | 11 +++--- PAM/ssh/include/rublon/static_string.hpp | 35 ++++++++++++------- PAM/ssh/include/rublon/websockets.hpp | 21 +++++------ 7 files changed, 46 insertions(+), 33 deletions(-) diff --git a/PAM/ssh/CMakeLists.txt b/PAM/ssh/CMakeLists.txt index 26dad44..3f7b726 100755 --- a/PAM/ssh/CMakeLists.txt +++ b/PAM/ssh/CMakeLists.txt @@ -128,7 +128,7 @@ FetchContent_Declare( RapidJSON URL https://github.com/Tencent/rapidjson/archive/refs/tags/v1.1.0.zip URL_HASH MD5=ceb1cf16e693a3170c173dc040a9d2bd - PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_LIST_DIR}/patches/rapidjson.patch + # PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_LIST_DIR}/patches/rapidjson.patch ) if(NOT RapidJSON_POPULATED) diff --git a/PAM/ssh/include/rublon/authentication_step_interface.hpp b/PAM/ssh/include/rublon/authentication_step_interface.hpp index 63f7ad3..89d0fa2 100644 --- a/PAM/ssh/include/rublon/authentication_step_interface.hpp +++ b/PAM/ssh/include/rublon/authentication_step_interface.hpp @@ -35,7 +35,7 @@ class AuthenticationStep { auto event = eventListener.listen(); if(event.status == transactionConfirmed ){ log(LogLevel::Debug, " Transaction confirmed"); - return AuthenticationStatus{AuthenticationStatus::Action::Confirmed, std::string{event.accessToken.value().c_str()}}; + return AuthenticationStatus{AuthenticationStatus::Action::Confirmed, event.accessToken.value().c_str()}; } log(LogLevel::Debug, " Transaction denied"); return AuthenticationStatus{AuthenticationStatus::Action::Denied}; diff --git a/PAM/ssh/include/rublon/core_handler.hpp b/PAM/ssh/include/rublon/core_handler.hpp index 1e88c78..46feb55 100755 --- a/PAM/ssh/include/rublon/core_handler.hpp +++ b/PAM/ssh/include/rublon/core_handler.hpp @@ -73,7 +73,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { public: CoreHandler() = delete; - CoreHandler(const Configuration & config) : _config{config}, _ws{std::make_unique(_config.apiServer)}, http{_config} { + 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(CoreHandler &&) noexcept = default; @@ -132,7 +132,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { tl::unexpected< Error > handleCoreException(std::string_view exceptionString) const { log(LogLevel::Debug, "TMP got core exception: %s", exceptionString.data() ); - // can happen only dyring check application step + // can happen only during check application step if(auto error = RublonCheckApplicationException::fromString(exceptionString); error.has_value()) return tl::unexpected{Error{error.value()}}; @@ -184,7 +184,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { bool createWSConnection(std::string_view tid) const { if(not _ws){ - _ws.reset(new WebSocket (_config.apiServer)); + _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/init.hpp b/PAM/ssh/include/rublon/init.hpp index df8026b..dd524e6 100644 --- a/PAM/ssh/include/rublon/init.hpp +++ b/PAM/ssh/include/rublon/init.hpp @@ -13,8 +13,6 @@ #include -[[deprecated]] extern std::string g_tid; - namespace rublon { template < class MethodSelect_t = MethodSelect > diff --git a/PAM/ssh/include/rublon/pam_action.hpp b/PAM/ssh/include/rublon/pam_action.hpp index cb35c98..44e341f 100644 --- a/PAM/ssh/include/rublon/pam_action.hpp +++ b/PAM/ssh/include/rublon/pam_action.hpp @@ -1,15 +1,18 @@ #pragma once +#include "rublon/static_string.hpp" +#include #include namespace rublon { class AuthenticationStatus { public: + using tokenT = std::optional< StaticString< 64 > >; enum class Action { Denied, Confirmed, Bypass }; - AuthenticationStatus(Action action, std::string authenticationToken = "") - : _action{action}, _authenticationToken{std::move(authenticationToken)} {} + AuthenticationStatus(Action action, const char * token = nullptr) + : _action{action}, _authenticationToken{token == nullptr ? tokenT{std::nullopt} : tokenT{token}} {} constexpr bool userAuthorized() const { return _action == Action::Confirmed; @@ -20,12 +23,12 @@ class AuthenticationStatus { } std::string_view accessToken() const { - return _authenticationToken; + return {_authenticationToken->c_str(), _authenticationToken->size()}; } private: Action _action; - std::string _authenticationToken; /// TODO dynamic mem + tokenT _authenticationToken; /// TODO dynamic mem }; } // namespace rublon diff --git a/PAM/ssh/include/rublon/static_string.hpp b/PAM/ssh/include/rublon/static_string.hpp index 5d0ac45..b78c14e 100644 --- a/PAM/ssh/include/rublon/static_string.hpp +++ b/PAM/ssh/include/rublon/static_string.hpp @@ -31,19 +31,22 @@ template < size_t N > class StaticString : public details::StaticStringBase { public: constexpr StaticString() = default; - constexpr StaticString(const char (&chars)[N]) : m_str(toStdArray(chars)) {} + constexpr StaticString(const char (&chars)[N]) : m_str(toStdArray(chars)), _size{N} {} - constexpr StaticString(std::array< const char, N > chars) : m_str(std::move(chars)) {} + constexpr StaticString(std::array< const char, N > chars) : m_str(std::move(chars)), _size{N} {} constexpr StaticString(const char * str) { - std::strncpy(m_str.data(), str, N); + _size = std::min(strlen(str), N); + std::memcpy(m_str.data(), str, _size); } void operator=(const char * str) { - std::strncpy(m_str.data(), str, N); + _size = std::min(strlen(str), N); + std::memcpy(m_str.data(), str, _size); } - + void operator=(std::string_view str) { - std::strncpy(m_str.data(), str.data(), N); + _size = std::min(str.size(), N); + std::memcpy(m_str.data(), str.data(), _size); } const char * c_str() const noexcept { @@ -67,16 +70,23 @@ class StaticString : public details::StaticStringBase { } std::size_t size() const { - return strlen(m_str.data()); + return _size; } constexpr std::size_t capacity() const noexcept { return N - 1; } - template < std::size_t M > - constexpr StaticString< N + M - 1 > operator+(const StaticString< M > & rhs) const { - return join(resize< N - 1 >(m_str), rhs.m_str); + StaticString< N > & operator+=(const char * rhs) { + auto remaining = capacity() - _size; + auto rhs_len = std::strlen(rhs); + auto copy_len = std::min(rhs_len, remaining); + + std::strncpy(data() + _size, rhs, copy_len); + _size += copy_len; + data()[_size] = '\0'; // null + + return *this; } template < std::size_t M > @@ -88,10 +98,11 @@ class StaticString : public details::StaticStringBase { private: std::array< char, N + 1 > m_str{}; + std::size_t _size; }; -template -bool operator==(const StaticString &lhs, const char* rhs){ +template < size_t N > +bool operator==(const StaticString< N > & lhs, const char * rhs) { return strcmp(lhs.c_str(), rhs) == 0; } diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index 5abc469..1ef4758 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -130,19 +130,21 @@ class WebSocket { } bool attachToTransactionConfirmationChannel(std::string_view transaction_id) { - /// TODO change to StaticString and += - std::array< char, 128 > subscribe_message{0}; - std::array< char, 128 > buf{0}; + StaticString< 128 > subscribe_message{}; + unsigned char buf[128 + LWS_PRE] = {}; - sprintf(subscribe_message.data(), R"msg(42["subscribe",{"channel":"transactionConfirmation.%s"}])msg", transaction_id.data()); - memcpy(&buf[LWS_PRE], subscribe_message.data(), strlen(subscribe_message.data())); + subscribe_message += R"msg(42["subscribe",{"channel":"transactionConfirmation.)msg"; + subscribe_message += transaction_id.data(); + subscribe_message += "}]"; - log(LogLevel::Debug, "WS send message: %s", subscribe_message.data()); - int bytes_sent = lws_write(wsi, ( unsigned char * ) &buf[LWS_PRE], strlen(subscribe_message.data()), LWS_WRITE_TEXT); + memcpy(buf + LWS_PRE, subscribe_message.data(), subscribe_message.size()); + + log(LogLevel::Debug, "WS send message: %s", subscribe_message.c_str()); + int bytes_sent = lws_write(wsi, buf + LWS_PRE, subscribe_message.size(), LWS_WRITE_TEXT); log(LogLevel::Debug, "WS send: %d bytes", bytes_sent); - if(bytes_sent < ( int ) strlen(subscribe_message.data())) { + if(bytes_sent < ( int ) subscribe_message.size()) { log(LogLevel::Error, "Failed to send subscribe message"); return false; } @@ -151,7 +153,6 @@ class WebSocket { bool AttachToCore(std::string_view tid) { log(LogLevel::Debug, "connecting to %s", ccinfo.address); - /// needed here only for rublon core api URL, so 1k fine lws_client_connect_via_info(&ccinfo); const int seconds = 10; @@ -209,7 +210,7 @@ class WebSocket { case LWS_CALLBACK_CLIENT_WRITEABLE: { // Perform the Socket.IO 4.x handshake (send `40` message) const std::string_view handshake = "40"; - unsigned char buf[64]; + unsigned char buf[64]={}; memcpy(&buf[LWS_PRE], handshake.data(), handshake.size()); lws_write(wsi, &buf[LWS_PRE], handshake.size(), LWS_WRITE_TEXT); log(LogLevel::Debug, "Sent Socket.IO handshake"); -- 2.45.2 From 8ea835c2f0239c28f1d0c19932f0ce40908ea729 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 30 May 2025 11:39:19 +0200 Subject: [PATCH 04/37] fix fileSHA256 function --- PAM/ssh/include/rublon/sign.hpp | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/PAM/ssh/include/rublon/sign.hpp b/PAM/ssh/include/rublon/sign.hpp index e1668a1..8365f12 100755 --- a/PAM/ssh/include/rublon/sign.hpp +++ b/PAM/ssh/include/rublon/sign.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -8,40 +9,37 @@ namespace rublon { +struct EVP_MD_CTX_deleter{ + void operator()(EVP_MD_CTX *ctx)const{ + EVP_MD_CTX_free(ctx); + } +}; + inline StaticString< SHA256_DIGEST_LENGTH * 2 > fileSHA256(const char * const path) { std::string fileContent; readFile(path, fileContent); StaticString< SHA256_DIGEST_LENGTH * 2 > xRublon{}; std::array< unsigned char, SHA256_DIGEST_LENGTH + 1 > hash{}; - int ret{}; - EVP_MD_CTX * ctx; - ctx = EVP_MD_CTX_new(); - - return 0; - if(ctx == NULL) + auto ctx = std::unique_ptr{EVP_MD_CTX_new()}; + + if(not ctx) goto out; // EVP_X methods return 1 on success, so does this function // Any values other than 1 denote error - ret = EVP_DigestInit(ctx, EVP_sha256()); - if(!ret) + if(not EVP_DigestInit(ctx.get(), EVP_sha256())) goto out; - - ret = EVP_DigestUpdate(ctx, fileContent.data(), fileContent.size()); - if(!ret) + + if(not EVP_DigestUpdate(ctx.get(), fileContent.data(), fileContent.size())) goto out; // Provide uint* instead of NULL to get nBytes written, 32 for SHA256 - ret = EVP_DigestFinal(ctx, hash.data(), NULL); - if(!ret) + if(not EVP_DigestFinal(ctx.get(), hash.data(), NULL)) goto out; out: - if(ctx != NULL) - EVP_MD_CTX_free(ctx); - for(unsigned int i = 0; i < SHA256_DIGEST_LENGTH; i++) sprintf(&xRublon[i * 2], "%02x", ( unsigned int ) hash[i]); @@ -51,7 +49,7 @@ out: // +1 for \0 inline StaticString< SHA256_DIGEST_LENGTH * 2 > signData(std::string_view data, std::string_view secretKey) { StaticString< SHA256_DIGEST_LENGTH * 2 > xRublon; - std::array< unsigned char, EVP_MAX_MD_SIZE > md; + std::array< unsigned char, EVP_MAX_MD_SIZE > md{}; unsigned int md_len{}; HMAC(EVP_sha256(), secretKey.data(), secretKey.size(), ( unsigned const char * ) data.data(), data.size(), md.data(), &md_len); -- 2.45.2 From e5cc1f619e5f2f90a9332479d347644f80ecf92b Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 30 May 2025 14:22:06 +0200 Subject: [PATCH 05/37] rewrite configuration reading module --- PAM/ssh/include/rublon/configuration.hpp | 331 ++++++++++++----------- PAM/ssh/include/rublon/core_handler.hpp | 3 +- PAM/ssh/include/rublon/curl.hpp | 35 ++- PAM/ssh/include/rublon/error.hpp | 2 + PAM/ssh/include/rublon/finish.hpp | 7 +- PAM/ssh/include/rublon/init.hpp | 7 +- PAM/ssh/include/rublon/utils.hpp | 63 ++++- PAM/ssh/include/rublon/websockets.hpp | 23 +- PAM/ssh/lib/pam.cpp | 6 +- 9 files changed, 274 insertions(+), 203 deletions(-) diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 5c335d0..1001bb1 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -1,14 +1,14 @@ #pragma once +#include +#include #include #include #include -#include -template < typename T > -constexpr bool is_static_string_v = std::is_base_of_v< rublon::details::StaticStringBase, T >; - -static_assert(is_static_string_v< rublon::StaticString< 32 > >); +#include +#include +#include namespace rublon { class ConfigurationFactory; @@ -16,11 +16,16 @@ class ConfigurationFactory; enum class FailMode { bypass, deny }; class Configuration { + private: + std::pmr::memory_resource * memoryResource; + public: + Configuration() : memoryResource{memory::default_resource()} {} + // change to StaticString - StaticString< 32 > systemToken{}; - StaticString< 32 > secretKey{}; - StaticString< 4096 > apiServer{}; + std::pmr::string systemToken{memoryResource}; + std::pmr::string secretKey{memoryResource}; + std::pmr::string apiServer{memoryResource}; int prompt{}; bool enablePasswdEmail{}; @@ -29,168 +34,36 @@ class Configuration { FailMode failMode{}; bool nonInteractiveMode{}; - StaticString< 8 > proxyType{}; - StaticString< 4096 > proxyServer{}; - StaticString< 256 > proxyUsername{}; - StaticString< 256 > proxyPass{}; - int proxyPort{}; - bool proxyAuthRequired{}; - bool proxyEnabled{}; + std::optional< std::pmr::string > proxyType{memoryResource}; + std::optional< std::pmr::string > proxyServer{memoryResource}; + std::optional< std::pmr::string > proxyUsername{memoryResource}; + std::optional< std::pmr::string > proxyPass{memoryResource}; + std::optional< int > proxyPort{}; + bool proxyAuthRequired{}; // defaulted + bool proxyEnabled{}; // defaulted }; -namespace { - template < class C, typename T > - T member_ptr_t(T C::*v); - template < typename T > - tl::expected< T, ConfigurationError > to(std::string_view); - - template < class T > - auto to_string(std::string_view arg) -> tl::expected< T, ConfigurationError > { - T value{}; - assert(arg.size() <= (value.size() - 1)); - std::memcpy(value.data(), arg.data(), arg.size()); - return value; - } - - template <> - auto to(std::string_view arg) -> tl::expected< bool, ConfigurationError > { - return conv::to_bool(arg); - } - template <> - auto to(std::string_view arg) -> tl::expected< int, ConfigurationError > { - return conv::to_uint32(arg).value_or(0); - } - - template <> - auto to(std::string_view arg) -> tl::expected< FailMode, ConfigurationError > { - if(arg == "bypass") - return FailMode::bypass; - if(arg == "deny") - return FailMode::deny; - return tl::unexpected{ConfigurationError{ConfigurationError::ErrorClass::BadFailMode}}; - } - - template < typename T > - auto parse(std::string_view arg) -> tl::expected< T, ConfigurationError > { - if(arg.empty()) { - return tl::unexpected{ConfigurationError::ErrorClass::Empty}; - } else { - if constexpr(is_static_string_v< T >) { - return to_string< T >(arg); - } else { - return to< T >(arg); - } - } - } - -} // namespace -struct Entry { - enum class Source { UserInput, DefaultValue }; - template < auto member > - static constexpr auto make_read_function() { - using pType = decltype(member_ptr_t(member)); - - return - [](const Entry * _this, Configuration * configuration, std::string_view userInput) -> tl::expected< Source, ConfigurationError > { - const auto setDefaultValue = [&](const ConfigurationError & error) -> tl::expected< Source, ConfigurationError > { - log(LogLevel::Warning, "applying user provided value for %s parameter, faild with %s", _this->name, error.what()); - if(_this->defaultValue != nullptr) { - configuration->*member = parse< pType >(_this->defaultValue).value(); - return Source::DefaultValue; - } else { - log(LogLevel::Error, "parameter %s has not been found and has no default value", _this->name); - if(userInput.empty()) - return tl::unexpected{ConfigurationError::ErrorClass::RequiredValueNotFound}; - else - return tl::unexpected{ConfigurationError::ErrorClass::BadInput}; - } - }; - - const auto saveValue = [&](const auto & value) -> tl::expected< Source, ConfigurationError > { - configuration->*member = value; - return Source::UserInput; - }; - - return parse< pType >(userInput).and_then(saveValue).or_else(setDefaultValue); - }; - } - - const char * name; - const char * defaultValue; - tl::expected< Source, ConfigurationError > (*_read)(const Entry * _this, Configuration * configuration, std::string_view userInput); - - bool read(Configuration * configuration, std::optional< std::string_view > userInput) const { - constexpr const auto emptyString = ""; - const auto logStored = [&](const auto & source) -> tl::expected< Source, ConfigurationError > { - rublon::log(LogLevel::Debug, - "Configuration parameter '%s' was set to '%s'%s", - this->name, - this->defaultValue, - source == Source::DefaultValue ? " (default)" : ""); - return source; - }; - - const auto logError = [&](const auto & error) -> tl::expected< Source, ConfigurationError > { - rublon::log(LogLevel::Error, - "Configuration parameter '%s' has no default value and is not provided in user configuraion, aborting", - this->name); - return tl::unexpected{error}; - }; - - return _read(this, configuration, userInput.value_or(emptyString)).and_then(logStored).or_else(logError).has_value(); - } -}; - -template < auto member > -constexpr auto make_entry(const char * name, const char * defaultValue) { - return Entry{name, defaultValue, Entry::make_read_function< member >()}; -} - -constexpr static inline std::array< Entry, 16 > configurationVariables = { - make_entry< &Configuration::logging >("logging", "true"), - make_entry< &Configuration::systemToken >("systemToken", nullptr), - make_entry< &Configuration::secretKey >("secretKey", nullptr), - make_entry< &Configuration::apiServer >("rublonApiServer", nullptr), - make_entry< &Configuration::prompt >("prompt", "1"), - make_entry< &Configuration::enablePasswdEmail >("enablePasswdEmail", "true"), - make_entry< &Configuration::autopushPrompt >("autopushPrompt", "false"), - make_entry< &Configuration::failMode >("failMode", "deny"), - make_entry< &Configuration::nonInteractiveMode >("nonInteractiveMode", "false"), - - make_entry< &Configuration::proxyType >("proxyType", nullptr), - make_entry< &Configuration::proxyServer >("proxyServer", nullptr), - make_entry< &Configuration::proxyUsername >("proxyUsername", nullptr), - make_entry< &Configuration::proxyPass >("proxyPass", nullptr), - make_entry< &Configuration::proxyPort >("proxyPort", "8080"), - make_entry< &Configuration::proxyAuthRequired >("proxyAuthRequired", "false"), - make_entry< &Configuration::proxyEnabled >("proxyEnabled", "false") // -}; - -class ConfigurationFactory { +class ConfigurationReader { public: - ConfigurationFactory() = default; + ConfigurationReader(std::pmr::memory_resource * memResource = memory::default_resource()) : memoryResource(memResource) {} - std::optional< Configuration > systemConfig() { - memory::MonotonicStackResource< 16 * 1024 > stackResource; - Configuration configuration{}; - - std::ifstream file(std::filesystem::path{"/etc/rublon.config"}); + // Load config from file path + bool loadFromFile(const std::string & filepath) { + using namespace memory::literals; + memory::MonotonicStackResource< 8_kB > stackResource; + std::ifstream file(filepath); if(not file.good()) - return std::nullopt; + return false; std::pmr::string line{&stackResource}; - line.reserve(100); - std::pmr::map< std::pmr::string, std::pmr::string > parameters{&stackResource}; - - const auto readParameterByName = [&](std::string_view name) -> std::optional< std::string_view > { - return parameters.count(name.data()) ? std::optional< std::string_view >{parameters.at(name.data())} : std::nullopt; - }; + line.reserve(8000); // allocate full stack to line while(std::getline(file, line)) { - std::pmr::string key{&stackResource}; - std::pmr::string value{&stackResource}; + std::pmr::string key{memoryResource}; + std::pmr::string value{memoryResource}; + details::trimInPlace(line); if(!line.length()) continue; @@ -201,15 +74,147 @@ class ConfigurationFactory { key = line.substr(0, posEqual); value = line.substr(posEqual + 1); - parameters[std::move(key)] = std::move(value); + keyValues[std::move(key)] = std::move(value); } - for(const auto & entry : configurationVariables) { - if(not entry.read(&configuration, readParameterByName(entry.name))) + return true; + } + + // Load values into Configuration object, with defaults provided + tl::expected< bool, ConfigurationError > applyTo(Configuration & config) { + // Helper lambdas for conversion + using string = std::pmr::string; + + auto getStringOpt = [&](const string & key) -> std::optional< std::pmr::string > { + auto it = keyValues.find(key); + if(it == keyValues.end()) { return std::nullopt; + } + return string{it->second.data(), it->second.size(), memoryResource}; + }; + + auto getStringReq = [&](const string & key) -> tl::expected< std::pmr::string, ConfigurationError > { + auto val = getStringOpt(key); + if(val.has_value()) + return std::move(val.value()); + return tl::unexpected{ConfigurationError::ErrorClass::RequiredValueNotFound}; + }; + + auto getInt = [&](const string & key) -> std::optional< int > { + auto it = keyValues.find(key); + if(it == keyValues.end()) + return std::nullopt; + return conv::to_uint32opt(it->second); + }; + + auto getBool = [&](const string & key) -> std::optional< bool > { + memory::MonotonicStackResource< 64 > memoryResource; + auto it = keyValues.find(key); + if(it == keyValues.end()) + return std::nullopt; + + if (it->second.size() > 5 ){ + log(LogLevel::Warning, "Configuration value %s is too long, please check", key.c_str()); + return std::nullopt; + } + + std::pmr::string val{&memoryResource}; + val = it->second; + std::transform(val.begin(), val.end(), val.begin(), [](unsigned char c) { return static_cast< char >(std::tolower(c)); }); + + if(val == "1" || val == "true" || val == "yes" || val == "on") + return true; + if(val == "0" || val == "false" || val == "no" || val == "off") + return false; + return std::nullopt; + }; + + auto getFailMode = [&](const string & key) -> std::optional< FailMode > { + auto it = keyValues.find(key); + if(it == keyValues.end()) + return std::nullopt; + auto val = it->second; + if(val == "bypass") + return FailMode::bypass; + if(val == "deny") + return FailMode::deny; + + return std::nullopt; + }; + + // Reading required fields + if(auto val = getStringReq("systemToken"); not val.has_value()) + return tl::unexpected{val.error()}; + else + config.systemToken = std::move(val.value()); + + if(auto val = getStringReq("secretKey"); not val.has_value()) + return tl::unexpected{val.error()}; + else + config.secretKey = std::move(val.value()); + + if(auto val = getStringReq("rublonApiServer"); not val.has_value()) + return tl::unexpected{val.error()}; + else + config.apiServer = std::move(val.value()); + + // optional + config.prompt = getInt("prompt").value_or(1); + config.enablePasswdEmail = getBool("enablePasswdEmail").value_or(true); + config.logging = getBool("logging").value_or(true); + config.autopushPrompt = getBool("autopushPrompt").value_or(false); + config.nonInteractiveMode = getBool("nonInteractiveMode").value_or(false); + config.failMode = getFailMode("failMode").value_or(FailMode::deny); + + // reading proxy configuration + config.proxyEnabled = getBool("proxyEnabled").value_or(false); + config.proxyType = getStringOpt("proxyType"); + config.proxyServer = getStringOpt("proxyServer"); + if(config.proxyEnabled) { + if(not config.proxyType) { + log(LogLevel::Error, "Proxy is enabled but proxy type is not set"); + return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration}; + } + if(not config.proxyServer) { + log(LogLevel::Error, "Proxy is enabled but proxy server is not set"); + return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration}; + } + } + config.proxyAuthRequired = getBool("proxyAuthRequired").value_or(false); + config.proxyUsername = getStringOpt("proxyUsername"); + config.proxyPass = getStringOpt("proxyPass"); + if(config.proxyAuthRequired) { + if(not config.proxyUsername) { + log(LogLevel::Error, "Proxy auth is required but proxy proxy username is not set"); + return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration}; + } + if(not config.proxyPass) { + log(LogLevel::Error, "Proxy is enabled but proxy password is not set,"); + return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration}; + } } - return configuration; + config.proxyPort = getInt("proxyPort").value_or(8080); + return true; + } + + private: + std::pmr::memory_resource * memoryResource; + std::pmr::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 46feb55..06ac82d 100755 --- a/PAM/ssh/include/rublon/core_handler.hpp +++ b/PAM/ssh/include/rublon/core_handler.hpp @@ -172,7 +172,8 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { signRequest(request); std::pmr::string uri{&memoryResource}; - uri += _config.apiServer.c_str(); + uri.reserve(_config.apiServer.size() + path.size() + 1); + uri += _config.apiServer; uri += path.data(); return http diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index 912aa91..ea70027 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -1,6 +1,7 @@ #pragma once #include "rublon/memory.hpp" +#include #include #include #include @@ -11,6 +12,7 @@ namespace rublon { + namespace { size_t WriteMemoryCallback(void * contents, size_t size, size_t nmemb, void * userp) { const size_t realsize = size * nmemb; @@ -60,7 +62,7 @@ class CURL { tl::expected< std::reference_wrapper< Response >, ConnectionError > request(std::string_view uri, const Request & request, Response & response) const { using namespace memory::literals; - memory::Monotonic_16k_HeapResource memoryResource; + memory::Monotonic_8k_HeapResource memoryResource; std::pmr::string response_data{&memoryResource}; response_data.reserve(4_kB); @@ -73,16 +75,20 @@ class CURL { // Optional: Build full proxy URL if proxy is enabled if (_config.proxyEnabled) { - std::pmr::string proxyUrl{&memoryResource}; - proxyUrl.reserve(4_kB); + // configuration reader check if proxy has needed fields + assert(_config.proxyType.has_value()); + assert(_config.proxyServer.has_value()); + std::pmr::string proxyUrl{&memoryResource}; + proxyUrl.reserve(conservative_estimate(_config.proxyType, _config.proxyServer, _config.proxyPort) + 10); + if (_config.proxyType == "http" || _config.proxyType == "https" || _config.proxyType == "socks4" || _config.proxyType == "socks5") { - proxyUrl = _config.proxyType.c_str(); + proxyUrl = *_config.proxyType; proxyUrl += "://"; - proxyUrl += _config.proxyServer.c_str(); + proxyUrl += *_config.proxyServer; if (_config.proxyPort > 0) { proxyUrl += ":"; - proxyUrl += std::to_string(_config.proxyPort); + proxyUrl += std::to_string(*_config.proxyPort); } curl_easy_setopt(curl.get(), CURLOPT_PROXY, proxyUrl.c_str()); @@ -96,12 +102,19 @@ class CURL { } if (_config.proxyAuthRequired) { + assert(_config.proxyUsername.has_value()); + assert(_config.proxyPass.has_value()); + std::pmr::string proxyAuth{&memoryResource}; - proxyAuth.reserve(1_kB); - _config.proxyUsername.c_str(); - proxyAuth += ":"; - proxyAuth += _config.proxyPass.c_str(); - + proxyAuth.reserve(conservative_estimate(_config.proxyUsername->size() + _config.proxyPass->size())); + + proxyAuth += *_config.proxyUsername; + if(_config.proxyPass->size()) { + // can proxy have name but no pass? + proxyAuth += ":"; + proxyAuth += *_config.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 21af816..79eee49 100755 --- a/PAM/ssh/include/rublon/error.hpp +++ b/PAM/ssh/include/rublon/error.hpp @@ -15,12 +15,14 @@ class ConfigurationError { RequiredValueNotFound, BadFailMode, BadInput, + BadConfiguration, Empty }; constexpr static auto errorClassPrettyName = make_array< std::string_view >( // "RequiredValueNotFound", "BadFailMode", "BadInput", + "BadConfiguration", "Empty"); constexpr static auto prettyName = "Configurtion Error"; diff --git a/PAM/ssh/include/rublon/finish.hpp b/PAM/ssh/include/rublon/finish.hpp index c943d2f..ad598eb 100644 --- a/PAM/ssh/include/rublon/finish.hpp +++ b/PAM/ssh/include/rublon/finish.hpp @@ -4,16 +4,17 @@ #include #include #include +#include #include namespace rublon { class Finish : public AuthenticationStep { const char * apiPath = "/api/transaction/credentials"; - const std::string _accessToken; + const std::string_view _accessToken; // void addAccessToken(Document & coreRequest) const { auto & alloc = coreRequest.GetAllocator(); - coreRequest.AddMember("accessToken", Value{_accessToken.c_str(), alloc}, alloc); + coreRequest.AddMember("accessToken", Value{_accessToken.data(), alloc}, alloc); } tl::expected< bool, Error > returnOk(const Document & /*coreResponse*/) const { @@ -23,7 +24,7 @@ class Finish : public AuthenticationStep { public: const char * name = "Finalization"; - Finish(Session & session, std::string accessToken) : AuthenticationStep(session), _accessToken{std::move(accessToken)} {} + Finish(Session & session, std::string_view accessToken) : AuthenticationStep(session), _accessToken{accessToken} {} template < typename Hander_t > tl::expected< bool, Error > handle(const CoreHandlerInterface< Hander_t > & coreHandler) const { diff --git a/PAM/ssh/include/rublon/init.hpp b/PAM/ssh/include/rublon/init.hpp index dd524e6..216816f 100644 --- a/PAM/ssh/include/rublon/init.hpp +++ b/PAM/ssh/include/rublon/init.hpp @@ -43,12 +43,11 @@ class Init : public AuthenticationStep { } void addParams(Document & coreRequest, const Pam_t & pam) const { - memory::MonotonicStackResource< 1024 > stackResource; - std::pmr::string releaseInfo{&stackResource}; + memory::MonotonicStackResource< 1024 > memoryResource; auto & alloc = coreRequest.GetAllocator(); - const auto os = details::osName(&stackResource); - const auto host = details::hostname(&stackResource); + const auto os = details::osName(&memoryResource); + const auto host = details::hostname(&memoryResource); if(os == "unknown") { log(LogLevel::Warning, "No OS information available"); diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index 9078dd4..973584a 100755 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -158,6 +159,14 @@ namespace conv { return tl::make_unexpected(Error::OutOfRange); } } + + inline std::optional< std::uint32_t> to_uint32opt(std::string_view userinput) noexcept { + try { + return std::stoi(userinput.data()); + } catch(...) { + return std::nullopt; + } + } } // namespace conv namespace details { @@ -176,6 +185,23 @@ namespace details { static inline std::string_view trim(std::string_view s) { return ltrim(rtrim(s)); } + + template + void trimInPlace(StrT & s) { + // Remove leading whitespace + size_t start = 0; + while(start < s.size() && isspace(static_cast< unsigned char >(s[start]))) + ++start; + + // Remove trailing whitespace + size_t end = s.size(); + while(end > start && isspace(static_cast< unsigned char >(s[end - 1]))) + --end; + + if(start > 0 || end < s.size()) { + s = s.substr(start, end - start); + } + } template < typename Headers > inline void headers(std::string_view data, Headers & headers) { @@ -197,7 +223,7 @@ namespace details { } std::pmr::string hostname(std::pmr::memory_resource * mr) { - std::pmr::string hostname{512, '\0', mr}; + std::pmr::string hostname{2048, '\0', mr}; if(gethostname(hostname.data(), hostname.size()) != 0) { log(LogLevel::Warning, "Hostname is not available"); return ""; @@ -207,18 +233,18 @@ namespace details { } std::pmr::string osName(std::pmr::memory_resource * mr) { - memory::MonotonicStackResource< 8 * 1024 > stackResource; + memory::Monotonic_8k_HeapResource memoryResource; std::ifstream file(std::filesystem::path{"/etc/os-release"}); if(not file.good()) return {"unknown", mr}; - std::pmr::string line{&stackResource}; + std::pmr::string line{&memoryResource}; + std::pmr::string _key{&memoryResource}; + std::pmr::string _value{&memoryResource}; line.reserve(100); - + while(std::getline(file, line)) { - std::pmr::string _key{&stackResource}; - std::pmr::string _value{&stackResource}; if(!line.length()) continue; @@ -266,4 +292,29 @@ constexpr std::array< Out, sizeof...(Types) > make_array(Types... names) { return {std::forward< Types >(names)...}; } +template std::size_t size_buffer(const T &item) { + using U = std::decay_t; + if constexpr (std::is_same_v) { + return strlen(item); + } else if constexpr(std::is_same_v< U, std::pmr::string > || std::is_same_v< U, std::string >) { + return item.size(); + } else if constexpr(std::is_integral_v< U > || std::is_floating_point_v< U >) { + return std::numeric_limits::digits; + } + return 0; +} + +template std::size_t size_buffer(const std::optional &item) { + if(item.has_value()) + return size_buffer(*item); + return 0; +} + +// min + 10% +template < typename... Args > +std::size_t conservative_estimate(const Args &... args) { + auto min = (size_buffer(args) + ...); + return min + min * 10 / 100; +} + } // namespace rublon diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index 1ef4758..e54761e 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -44,10 +44,8 @@ class WebSocket { RublonEventData * currentEvent{nullptr}; public: - WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer.c_str(), _config.get().apiServer.size()} { - using namespace memory::literals; - memory::Monotonic_8k_HeapResource memoryResource; - const auto & cfg = _config.get(); + WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer} { + const auto & cfg = _config.get(); // only a alias to not use _config.get() all the time auto lws_log_emit = [](int level, const char * line) { LogLevel rlevel{}; @@ -77,23 +75,27 @@ class WebSocket { context = lws_create_context(&info); if(cfg.proxyEnabled && (cfg.proxyType == "http" || cfg.proxyType == "https")) { + assert(cfg.proxyType.has_value()); + assert(cfg.proxyServer.has_value()); + + memory::Monotonic_8k_HeapResource memoryResource; std::pmr::string proxyUrl{&memoryResource}; - proxyUrl.reserve(4_kB); + proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyServer, cfg.proxyPort) + 10); - proxyUrl += cfg.proxyType.data(); + proxyUrl += cfg.proxyType->data(); proxyUrl += "://"; if(cfg.proxyAuthRequired) { - proxyUrl += cfg.proxyUsername.c_str(); + proxyUrl += *cfg.proxyUsername; proxyUrl += ":"; - proxyUrl += cfg.proxyPass.c_str(); + proxyUrl += *cfg.proxyPass; proxyUrl += "@"; } - proxyUrl += cfg.proxyServer.c_str(); + proxyUrl += *cfg.proxyServer; if(cfg.proxyPort > 0) { proxyUrl += ":"; - proxyUrl += std::to_string(cfg.proxyPort); + proxyUrl += std::to_string(*cfg.proxyPort); } // Set environment variable for libwebsockets to pick up @@ -101,7 +103,6 @@ class WebSocket { } const std::string_view prefix = "https://"; - if(urlv.substr(0, prefix.size()) == prefix) urlv.remove_prefix(prefix.size()); diff --git a/PAM/ssh/lib/pam.cpp b/PAM/ssh/lib/pam.cpp index ee3b197..8f3b8dc 100644 --- a/PAM/ssh/lib/pam.cpp +++ b/PAM/ssh/lib/pam.cpp @@ -83,9 +83,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu 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.handle(CH); + Finish{session.value(), status.accessToken()}.handle(CH); } return status; }; @@ -101,7 +99,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu { CheckApplication ca; const auto & config = session.value().config(); - const auto ret = ca.call(CH, {config.systemToken.data(), config.systemToken.size()}).or_else(mapError); + 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"); return PAM_MAXTRIES; -- 2.45.2 From aa334ea60ad7a1b4f8816c95a0164fe798974cb0 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Tue, 3 Jun 2025 13:02:57 +0200 Subject: [PATCH 06/37] fix formatting --- PAM/ssh/include/rublon/configuration.hpp | 34 ++++++++++-------- PAM/ssh/include/rublon/curl.hpp | 36 ++++++++++--------- .../include/rublon/method/method_select.hpp | 8 +++-- PAM/ssh/include/rublon/pam_action.hpp | 8 +++-- 4 files changed, 49 insertions(+), 37 deletions(-) diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 1001bb1..9e7abd0 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -1,15 +1,20 @@ #pragma once -#include -#include #include +#include #include #include +#include #include #include #include +#include +#include +#include +#include + namespace rublon { class ConfigurationFactory; @@ -20,7 +25,7 @@ class Configuration { std::pmr::memory_resource * memoryResource; public: - Configuration() : memoryResource{memory::default_resource()} {} + Configuration(std::pmr::memory_resource * mr = memory::default_resource()) : memoryResource{mr} {} // change to StaticString std::pmr::string systemToken{memoryResource}; @@ -43,16 +48,15 @@ class Configuration { bool proxyEnabled{}; // defaulted }; - class ConfigurationReader { public: ConfigurationReader(std::pmr::memory_resource * memResource = memory::default_resource()) : memoryResource(memResource) {} // Load config from file path - bool loadFromFile(const std::string & filepath) { + bool loadFromFile(std::string_view filepath) { using namespace memory::literals; memory::MonotonicStackResource< 8_kB > stackResource; - std::ifstream file(filepath); + std::ifstream file(filepath.data()); if(not file.good()) return false; @@ -73,7 +77,7 @@ class ConfigurationReader { auto posEqual = line.find('='); key = line.substr(0, posEqual); value = line.substr(posEqual + 1); - + keyValues[std::move(key)] = std::move(value); } @@ -84,7 +88,7 @@ class ConfigurationReader { tl::expected< bool, ConfigurationError > applyTo(Configuration & config) { // Helper lambdas for conversion using string = std::pmr::string; - + auto getStringOpt = [&](const string & key) -> std::optional< std::pmr::string > { auto it = keyValues.find(key); if(it == keyValues.end()) { @@ -112,12 +116,12 @@ class ConfigurationReader { auto it = keyValues.find(key); if(it == keyValues.end()) return std::nullopt; - - if (it->second.size() > 5 ){ + + if(it->second.size() > 5) { log(LogLevel::Warning, "Configuration value %s is too long, please check", key.c_str()); return std::nullopt; } - + std::pmr::string val{&memoryResource}; val = it->second; std::transform(val.begin(), val.end(), val.begin(), [](unsigned char c) { return static_cast< char >(std::tolower(c)); }); @@ -200,7 +204,7 @@ class ConfigurationReader { private: std::pmr::memory_resource * memoryResource; - std::pmr::map< std::pmr::string, std::pmr::string > keyValues{memoryResource}; + std::pmr::unordered_map< std::pmr::string, std::pmr::string > keyValues{memoryResource}; }; class ConfigurationFactory { @@ -208,10 +212,10 @@ class ConfigurationFactory { ConfigurationFactory() = default; std::optional< Configuration > systemConfig() { - std::optional< Configuration > conf{Configuration{}}; - ConfigurationReader reader; + std::optional< Configuration > conf{}; + ConfigurationReader reader{}; reader.loadFromFile("/etc/rublon.config"); - if(auto ok = reader.applyTo(conf.value()); not ok.has_value()){ + if(auto ok = reader.applyTo(conf.value()); not ok.has_value()) { return std::nullopt; } return conf.value(); diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index ea70027..0771af1 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -2,9 +2,9 @@ #include "rublon/memory.hpp" #include +#include #include #include -#include #include @@ -12,7 +12,6 @@ namespace rublon { - namespace { size_t WriteMemoryCallback(void * contents, size_t size, size_t nmemb, void * userp) { const size_t realsize = size * nmemb; @@ -55,9 +54,11 @@ struct Response { class CURL { std::unique_ptr< ::CURL, void (*)(::CURL *) > curl; - const Configuration &_config; + const Configuration & _config; + public: - CURL(const Configuration &config) : curl{std::unique_ptr< ::CURL, void (*)(::CURL *) >(curl_easy_init(), curl_easy_cleanup)}, _config{config} {} + CURL(const Configuration & config) + : curl{std::unique_ptr< ::CURL, void (*)(::CURL *) >(curl_easy_init(), curl_easy_cleanup)}, _config{config} {} tl::expected< std::reference_wrapper< Response >, ConnectionError > request(std::string_view uri, const Request & request, Response & response) const { @@ -72,45 +73,46 @@ class CURL { log(LogLevel::Debug, "%s header: %s: %s", "CURL", header.first.c_str(), header.second.c_str()); curl_headers.reset(curl_slist_append(curl_headers.release(), (header.first + ": " + header.second).c_str())); }); - + // Optional: Build full proxy URL if proxy is enabled - if (_config.proxyEnabled) { + if(_config.proxyEnabled) { // configuration reader check if proxy has needed fields assert(_config.proxyType.has_value()); assert(_config.proxyServer.has_value()); - + std::pmr::string proxyUrl{&memoryResource}; proxyUrl.reserve(conservative_estimate(_config.proxyType, _config.proxyServer, _config.proxyPort) + 10); - if (_config.proxyType == "http" || _config.proxyType == "https" || _config.proxyType == "socks4" || _config.proxyType == "socks5") { + if(_config.proxyType == "http" || _config.proxyType == "https" || _config.proxyType == "socks4" || + _config.proxyType == "socks5") { proxyUrl = *_config.proxyType; proxyUrl += "://"; proxyUrl += *_config.proxyServer; - if (_config.proxyPort > 0) { + if(_config.proxyPort > 0) { proxyUrl += ":"; proxyUrl += std::to_string(*_config.proxyPort); } - + curl_easy_setopt(curl.get(), CURLOPT_PROXY, proxyUrl.c_str()); - - if (_config.proxyType == "socks4") { + + if(_config.proxyType == "socks4") { curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); - } else if (_config.proxyType == "socks5") { + } else if(_config.proxyType == "socks5") { curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); } else { curl_easy_setopt(curl.get(), CURLOPT_PROXYTYPE, CURLPROXY_HTTP); } - - if (_config.proxyAuthRequired) { + + if(_config.proxyAuthRequired) { assert(_config.proxyUsername.has_value()); assert(_config.proxyPass.has_value()); std::pmr::string proxyAuth{&memoryResource}; - proxyAuth.reserve(conservative_estimate(_config.proxyUsername->size() + _config.proxyPass->size())); + proxyAuth.reserve(conservative_estimate(_config.proxyUsername, _config.proxyPass)); proxyAuth += *_config.proxyUsername; if(_config.proxyPass->size()) { - // can proxy have name but no pass? + // can proxy have name but no pass? proxyAuth += ":"; proxyAuth += *_config.proxyPass; } diff --git a/PAM/ssh/include/rublon/method/method_select.hpp b/PAM/ssh/include/rublon/method/method_select.hpp index 616f821..0117f33 100644 --- a/PAM/ssh/include/rublon/method/method_select.hpp +++ b/PAM/ssh/include/rublon/method/method_select.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -129,12 +130,15 @@ class MethodSelect { int _prompts; bool _autopushPrompt; - std::vector< std::string > _methodsAvailable; // TODO pmr + std::pmr::memory_resource * _mr; + + // method name is really short, there is almost no chance that thos strings will allocate + std::pmr::vector< std::pmr::string > _methodsAvailable; public: template < typename Array_t > MethodSelect(Session & session, const Array_t & methodsEnabledInCore, int prompts, bool autopushPrompt) - : _session{session}, _prompts{prompts}, _autopushPrompt{autopushPrompt} { + : _session{session}, _prompts{prompts}, _autopushPrompt{autopushPrompt}, _mr{memory::default_resource()}, _methodsAvailable{_mr} { rublon::log(LogLevel::Debug, "Checking what methods from core are supported"); using namespace std::string_view_literals; _methodsAvailable.reserve(std::size(methodsEnabledInCore)); diff --git a/PAM/ssh/include/rublon/pam_action.hpp b/PAM/ssh/include/rublon/pam_action.hpp index 44e341f..c7aa97f 100644 --- a/PAM/ssh/include/rublon/pam_action.hpp +++ b/PAM/ssh/include/rublon/pam_action.hpp @@ -8,11 +8,11 @@ namespace rublon { class AuthenticationStatus { public: - using tokenT = std::optional< StaticString< 64 > >; + using Token_t = std::optional< StaticString< 64 > >; enum class Action { Denied, Confirmed, Bypass }; AuthenticationStatus(Action action, const char * token = nullptr) - : _action{action}, _authenticationToken{token == nullptr ? tokenT{std::nullopt} : tokenT{token}} {} + : _action{action}, _authenticationToken{token == nullptr ? Token_t{std::nullopt} : Token_t{token}} {} constexpr bool userAuthorized() const { return _action == Action::Confirmed; @@ -23,12 +23,14 @@ class AuthenticationStatus { } std::string_view accessToken() const { + if(not _authenticationToken) + return ""; return {_authenticationToken->c_str(), _authenticationToken->size()}; } private: Action _action; - tokenT _authenticationToken; /// TODO dynamic mem + Token_t _authenticationToken; }; } // namespace rublon -- 2.45.2 From 19e117e39bc3eb61ac5a18aa26d7f929d7f144c0 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Tue, 3 Jun 2025 14:02:37 +0200 Subject: [PATCH 07/37] fix bad optional access --- PAM/ssh/include/rublon/configuration.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 9e7abd0..30f03be 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -212,7 +211,7 @@ class ConfigurationFactory { ConfigurationFactory() = default; std::optional< Configuration > systemConfig() { - std::optional< Configuration > conf{}; + std::optional< Configuration > conf{Configuration{}}; ConfigurationReader reader{}; reader.loadFromFile("/etc/rublon.config"); if(auto ok = reader.applyTo(conf.value()); not ok.has_value()) { -- 2.45.2 From 65e3a5a73c6a52517685d505cce0650529cd27d9 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 4 Jun 2025 09:05:49 +0200 Subject: [PATCH 08/37] fix configuration check regresion --- PAM/ssh/include/rublon/configuration.hpp | 38 ++++++++++++++++-------- PAM/ssh/include/rublon/error.hpp | 2 ++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 30f03be..92fd275 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -76,7 +76,7 @@ class ConfigurationReader { auto posEqual = line.find('='); key = line.substr(0, posEqual); value = line.substr(posEqual + 1); - + keyValues[std::move(key)] = std::move(value); } @@ -84,10 +84,10 @@ class ConfigurationReader { } // Load values into Configuration object, with defaults provided - tl::expected< bool, ConfigurationError > applyTo(Configuration & config) { - // Helper lambdas for conversion + tl::expected< bool, ConfigurationError > applyTo(Configuration & config) const { using string = std::pmr::string; + // Helper lambdas for conversion auto getStringOpt = [&](const string & key) -> std::optional< std::pmr::string > { auto it = keyValues.find(key); if(it == keyValues.end()) { @@ -98,8 +98,12 @@ class ConfigurationReader { auto getStringReq = [&](const string & key) -> tl::expected< std::pmr::string, ConfigurationError > { auto val = getStringOpt(key); - if(val.has_value()) + if(val.has_value()) { + if(val->empty()) { + return tl::unexpected{ConfigurationError::ErrorClass::RequiredValueEmpty}; + } return std::move(val.value()); + } return tl::unexpected{ConfigurationError::ErrorClass::RequiredValueNotFound}; }; @@ -145,6 +149,14 @@ class ConfigurationReader { return std::nullopt; }; + /// NOTE: + // getStringOpt can return a valid empty string, for example configuration entry like + // option= + // will return a optional val which contains empty string. + // getStringReq on the other hand, returns error in case when + // * configuration is not found -> RequiredValueNotFound + // * configuration value is empty -> RequiredValueEmpty + // Reading required fields if(auto val = getStringReq("systemToken"); not val.has_value()) return tl::unexpected{val.error()}; @@ -161,7 +173,7 @@ class ConfigurationReader { else config.apiServer = std::move(val.value()); - // optional + // optional configuration options config.prompt = getInt("prompt").value_or(1); config.enablePasswdEmail = getBool("enablePasswdEmail").value_or(true); config.logging = getBool("logging").value_or(true); @@ -174,12 +186,12 @@ class ConfigurationReader { config.proxyType = getStringOpt("proxyType"); config.proxyServer = getStringOpt("proxyServer"); if(config.proxyEnabled) { - if(not config.proxyType) { - log(LogLevel::Error, "Proxy is enabled but proxy type is not set"); + if(not config.proxyType or config.proxyType->empty()) { + log(LogLevel::Error, "Proxy is enabled but proxy type is not present or empty"); return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration}; } - if(not config.proxyServer) { - log(LogLevel::Error, "Proxy is enabled but proxy server is not set"); + if(not config.proxyServer or config.proxyServer->empty()) { + log(LogLevel::Error, "Proxy is enabled but proxy server is not present or empty"); return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration}; } } @@ -187,12 +199,14 @@ class ConfigurationReader { config.proxyUsername = getStringOpt("proxyUsername"); config.proxyPass = getStringOpt("proxyPass"); if(config.proxyAuthRequired) { - if(not config.proxyUsername) { - log(LogLevel::Error, "Proxy auth is required but proxy proxy username is not set"); + if(not config.proxyUsername or config.proxyUsername->empty()) { + log(LogLevel::Error, "Proxy auth is required but proxy proxy username is not present or empty"); return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration}; } if(not config.proxyPass) { - log(LogLevel::Error, "Proxy is enabled but proxy password is not set,"); + log(LogLevel::Error, + "Proxy is enabled but proxy password is not present, " + "if no password is required add an empty entry to configuration file"); return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration}; } } diff --git a/PAM/ssh/include/rublon/error.hpp b/PAM/ssh/include/rublon/error.hpp index 79eee49..4d9b41a 100755 --- a/PAM/ssh/include/rublon/error.hpp +++ b/PAM/ssh/include/rublon/error.hpp @@ -13,6 +13,7 @@ class ConfigurationError { public: enum class ErrorClass { // RequiredValueNotFound, + RequiredValueEmpty, BadFailMode, BadInput, BadConfiguration, @@ -20,6 +21,7 @@ class ConfigurationError { }; constexpr static auto errorClassPrettyName = make_array< std::string_view >( // "RequiredValueNotFound", + "RequiredValueEmpty", "BadFailMode", "BadInput", "BadConfiguration", -- 2.45.2 From 161ba94f457223cf29d0f02cc44c462d4f27b4ae Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 4 Jun 2025 11:19:30 +0200 Subject: [PATCH 09/37] 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) -- 2.45.2 From 5ab6a47efba5ba40f58a01a67b62eeb52797659f Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 4 Jun 2025 12:44:44 +0200 Subject: [PATCH 10/37] Make everything in core connector memory resource aware --- PAM/ssh/bin/rublon_application.cpp | 3 +- PAM/ssh/include/rublon/check_application.hpp | 4 +- PAM/ssh/include/rublon/configuration.hpp | 14 ++- PAM/ssh/include/rublon/core_handler.hpp | 9 +- PAM/ssh/include/rublon/curl.hpp | 2 +- PAM/ssh/include/rublon/json.hpp | 4 +- PAM/ssh/include/rublon/memory.hpp | 37 +++++--- .../rublon/method/passcode_based_auth.hpp | 6 +- PAM/ssh/include/rublon/rublon.hpp | 4 +- PAM/ssh/include/rublon/session.hpp | 88 ++++++++++++++++++- PAM/ssh/include/rublon/utils.hpp | 2 +- 11 files changed, 131 insertions(+), 42 deletions(-) diff --git a/PAM/ssh/bin/rublon_application.cpp b/PAM/ssh/bin/rublon_application.cpp index 579f879..8dfde4a 100644 --- a/PAM/ssh/bin/rublon_application.cpp +++ b/PAM/ssh/bin/rublon_application.cpp @@ -62,8 +62,7 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { auto finalizeTransaction = [&](const AuthenticationStatus & status) mutable -> tl::expected< AuthenticationStatus, Error > { if(status.userAuthorized()) { - auto tok = std::string{status.accessToken().data()}; - Finish finish{session, std::move(tok)}; + Finish finish{session, status.accessToken()}; finish.handle(CH); } return status; diff --git a/PAM/ssh/include/rublon/check_application.hpp b/PAM/ssh/include/rublon/check_application.hpp index 87590b4..6817702 100644 --- a/PAM/ssh/include/rublon/check_application.hpp +++ b/PAM/ssh/include/rublon/check_application.hpp @@ -171,8 +171,8 @@ class CheckApplication { public: tl::expected< int, Error > call(const CoreHandler_t & coreHandler, std::string_view systemToken) const { - memory::Monotonic_1k_Resource mr; - RapidJSONPMRStackAlloc< 2048 > alloc{}; + memory::MonotonicStack_1k_Resource mr; + RapidJSONPMRAlloc alloc{&mr}; 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 32260b6..6c9765f 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -50,24 +50,22 @@ class Configuration { class ConfigurationReader { public: ConfigurationReader(std::pmr::memory_resource * memResource, std::string_view filepath) : memoryResource(memResource) { + keyValues.reserve(20); loadFromFile(filepath); } // Load config from file path void loadFromFile(std::string_view filepath) { using namespace memory::literals; - memory::MonotonicStackResource< 4_kB > stackResource; + memory::MonotonicStack_1k_Resource memoryResource; std::ifstream file(filepath.data()); if(not file.good()) return ; - std::pmr::string line{&stackResource}; - line.reserve(4000); // allocate full stack to line + std::pmr::string line{&memoryResource}; + line.reserve(300); while(std::getline(file, line)) { - std::pmr::string key{memoryResource}; - std::pmr::string value{memoryResource}; - details::trimInPlace(line); if(!line.length()) continue; @@ -76,8 +74,8 @@ class ConfigurationReader { continue; auto posEqual = line.find('='); - key = line.substr(0, posEqual); - value = line.substr(posEqual + 1); + std::pmr::string key {line.substr(0, posEqual),&memoryResource}; + std::pmr::string value{line.substr(posEqual + 1), &memoryResource}; keyValues[std::move(key)] = std::move(value); } diff --git a/PAM/ssh/include/rublon/core_handler.hpp b/PAM/ssh/include/rublon/core_handler.hpp index 4ebba2f..d252949 100755 --- a/PAM/ssh/include/rublon/core_handler.hpp +++ b/PAM/ssh/include/rublon/core_handler.hpp @@ -157,24 +157,23 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { } tl::expected< Document, Error > request(RapidJSONPMRAlloc & mr, std::string_view path, const Document & body) const { - memory::Monotonic_8k_Resource memoryResource; + memory::Monotonic_4k_Resource memoryResource; const auto validateSignature = [&](const auto & arg) { return this->validateSignature(arg); }; const auto validateResponse = [&](const auto & arg) { return this->validateResponse(mr, arg); }; const auto handleError = [&](const auto & error) { return this->handleError(error); }; - const auto pmrs = [&](const auto & txt) { return std::pmr::string{txt, &memoryResource}; }; Request request{&memoryResource}; Response response{&memoryResource}; - request.headers["Content-Type"] = pmrs("application/json"); - request.headers["Accept"] = pmrs("application/json"); + request.headers["Content-Type"] = "application/json"; + request.headers["Accept"] = "application/json"; stringifyTo(body, request.body); signRequest(request); std::pmr::string uri{&memoryResource}; - uri.reserve(config().apiServer.size() + path.size() + 1); + uri.reserve(conservative_estimate(config().apiServer, path.size())); uri += config().apiServer; uri += path.data(); diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index e991f04..eb020a3 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -23,7 +23,7 @@ namespace { struct Request { std::pmr::memory_resource * _mr; - + std::pmr::map< std::pmr::string, std::pmr::string > headers; std::pmr::string body; diff --git a/PAM/ssh/include/rublon/json.hpp b/PAM/ssh/include/rublon/json.hpp index d566294..7207bde 100644 --- a/PAM/ssh/include/rublon/json.hpp +++ b/PAM/ssh/include/rublon/json.hpp @@ -122,8 +122,8 @@ struct FileWriter { template < typename T > static void stringifyTo(const Document & body, T & to) { - memory::Monotonic_1k_Resource tmpResource; - RapidJSONPMRAlloc alloc{&tmpResource}; + memory::MonotonicStack_1k_Resource memoryResource; + RapidJSONPMRAlloc alloc{&memoryResource}; StringWriter< T > s{to}; rapidjson::Writer< StringWriter< T >, rapidjson::UTF8<>, rapidjson::UTF8<>, RapidJSONPMRAlloc > writer{s, &alloc}; body.Accept(writer); diff --git a/PAM/ssh/include/rublon/memory.hpp b/PAM/ssh/include/rublon/memory.hpp index 29960ae..6d12ef7 100644 --- a/PAM/ssh/include/rublon/memory.hpp +++ b/PAM/ssh/include/rublon/memory.hpp @@ -1,15 +1,16 @@ #pragma once +#include #include namespace rublon { namespace memory { - namespace literals{ + namespace literals { constexpr std::uint64_t operator"" _kB(unsigned long long kilobytes) { return kilobytes * 1024ULL; } - } - + } // namespace literals + struct default_memory_resource { static inline std::pmr::memory_resource * _mr = std::pmr::get_default_resource(); }; @@ -22,14 +23,6 @@ namespace memory { return default_memory_resource{}._mr; } - template < std::size_t N > - class MonotonicStackResource : public std::pmr::monotonic_buffer_resource { - char _buffer[N]; - - public: - MonotonicStackResource() : std::pmr::monotonic_buffer_resource{_buffer, N, std::pmr::null_memory_resource()} {} - }; - class MonotonicResourceBase { public: std::pmr::memory_resource * _upstream{}; @@ -47,15 +40,31 @@ namespace memory { template < std::size_t N > class MonotonicResource : MonotonicResourceBase, public std::pmr::monotonic_buffer_resource { public: - MonotonicResource() - : MonotonicResourceBase{N}, std::pmr::monotonic_buffer_resource{this->_buffer, this->_size, default_resource()} {} + MonotonicResource(std::pmr::memory_resource * mr = default_resource()) + : MonotonicResourceBase{N}, std::pmr::monotonic_buffer_resource{this->_buffer, this->_size, mr} {} }; + template < std::size_t N > + class MonotonicStackResourceBase { + public: + char _buffer[N]; + }; + + template < std::size_t N > + class MonotonicStackResource : MonotonicStackResourceBase< N >, public std::pmr::monotonic_buffer_resource { + public: + MonotonicStackResource() + : MonotonicStackResourceBase< N >{}, std::pmr::monotonic_buffer_resource{this->_buffer, N, default_resource()} {} + }; + + using MonotonicStack_1k_Resource = MonotonicStackResource< 1 * 1024 >; + using MonotonicStack_2k_Resource = MonotonicStackResource< 2 * 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/passcode_based_auth.hpp b/PAM/ssh/include/rublon/method/passcode_based_auth.hpp index 36fdfc8..ce1c7a5 100644 --- a/PAM/ssh/include/rublon/method/passcode_based_auth.hpp +++ b/PAM/ssh/include/rublon/method/passcode_based_auth.hpp @@ -1,5 +1,6 @@ #pragma once +#include "rublon/memory.hpp" #include #include @@ -59,8 +60,9 @@ class PasscodeBasedAuth : public AuthenticationStep { tl::expected< std::reference_wrapper< Document >, Error > readPasscode(Document & body, const Pam_t & pam) const { /// TODO assert in interactive mode - auto & alloc = body.GetAllocator(); - auto vericode = pam.scan([](const char * userInput) { return std::string{userInput}; }, _userMessage); + memory::MonotonicStackResource< 100 > memoryResource; + auto & alloc = body.GetAllocator(); + auto vericode = pam.scan([&](const char * userInput) { return std::pmr::string{userInput, &memoryResource}; }, _userMessage); if(hasValidLength(vericode) and hasValidCharacters(vericode)) { Value confirmFieldValue(_confirmField, alloc); diff --git a/PAM/ssh/include/rublon/rublon.hpp b/PAM/ssh/include/rublon/rublon.hpp index 4374abb..8632cc2 100755 --- a/PAM/ssh/include/rublon/rublon.hpp +++ b/PAM/ssh/include/rublon/rublon.hpp @@ -14,8 +14,8 @@ class RublonFactory { public: tl::expected< void, Error > initializeSession(Session & session) { log(LogLevel::Debug, "Configuration read start"); - memory::Monotonic_16k_Resource heap; - ConfigurationReader reader{&heap, "/etc/rublon.config"}; + memory::MonotonicStack_2k_Resource memory_resource; + ConfigurationReader reader{&memory_resource, "/etc/rublon.config"}; if(auto ok = reader.applyTo(session.config()); not ok.has_value()) { log(LogLevel::Warning, "Configuration contains errors"); diff --git a/PAM/ssh/include/rublon/session.hpp b/PAM/ssh/include/rublon/session.hpp index c5abac3..21bae4c 100644 --- a/PAM/ssh/include/rublon/session.hpp +++ b/PAM/ssh/include/rublon/session.hpp @@ -1,5 +1,6 @@ #pragma once +#include "rublon/memory.hpp" #include #include #include @@ -9,9 +10,91 @@ #include #include + namespace rublon { -class Session { + +// class LoggingMemoryResource : public std::pmr::memory_resource { +// public: +// LoggingMemoryResource(std::pmr::memory_resource* upstream = std::pmr::get_default_resource()) +// : upstream_(upstream), allocated_bytes_(0) +// { +// pid_t pid = getpid(); +// std::ostringstream filename; +// filename << "/tmp/memory" << pid << ".log"; +// log_file_.open(filename.str(), std::ios::out | std::ios::trunc); + +// if (!log_file_) { +// throw std::runtime_error("Failed to open log file"); +// } +// log("Memory logging started."); +// } + +// ~LoggingMemoryResource() override { +// log("Memory logging ended."); +// log_file_.close(); +// } + +// protected: +// void* do_allocate(std::size_t bytes, std::size_t alignment) override { +// std::lock_guard lock(mutex_); +// void* ptr = upstream_->allocate(bytes, alignment); +// allocated_bytes_ += bytes; +// active_allocations_[ptr] = bytes; + +// log("ALLOC", ptr, bytes, alignment); +// return ptr; +// } + +// void do_deallocate(void* ptr, std::size_t bytes, std::size_t alignment) override { +// std::lock_guard lock(mutex_); +// auto it = active_allocations_.find(ptr); +// if (it != active_allocations_.end()) { +// allocated_bytes_ -= it->second; +// active_allocations_.erase(it); +// } + +// log("FREE", ptr, bytes, alignment); +// upstream_->deallocate(ptr, bytes, alignment); +// } + +// bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { +// return this == &other; +// } + +// private: +// std::pmr::memory_resource* upstream_; +// std::ofstream log_file_; +// std::mutex mutex_; +// std::size_t allocated_bytes_; +// std::map active_allocations_; + +// void log(const std::string& action, void* ptr = nullptr, std::size_t bytes = 0, std::size_t alignment = 0) { +// auto now = std::chrono::system_clock::now(); +// auto now_time = std::chrono::system_clock::to_time_t(now); +// log_file_ << std::put_time(std::localtime(&now_time), "%F %T") +// << " | " << std::setw(6) << action; + +// if (ptr) { +// log_file_ << " | ptr=" << ptr << " bytes=" << bytes << " align=" << alignment; +// } + +// log_file_ << " | total=" << allocated_bytes_ << " bytes\n"; +// log_file_.flush(); +// } +// }; + +// std::pmr::unsynchronized_pool_resource resource{}; +// LoggingMemoryResource mr{&resource}; + +class DefaultResource { + public: + DefaultResource() { + // memory::set_default_resource(&mr); + } +}; + +class Session :public DefaultResource { std::pmr::memory_resource * mr; const Pam_t & _pam; Configuration _config; @@ -22,8 +105,7 @@ class Session { CoreHandler_t _coreHandler; /// TODO log public: - Session(const Pam_t & pam) - : mr{memory::default_resource()}, _pam{pam}, _config{mr}, _coreHandler{_config} { + Session(const Pam_t & pam) : DefaultResource{}, mr{memory::default_resource()}, _pam{pam}, _config{mr}, _coreHandler{_config} { details::initLog(); } diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index 4886b2e..599f111 100755 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -234,7 +234,7 @@ namespace details { } std::pmr::string osName(std::pmr::memory_resource * mr) { - memory::Monotonic_8k_Resource memoryResource; + memory::Monotonic_1k_Resource memoryResource; std::ifstream file(std::filesystem::path{"/etc/os-release"}); if(not file.good()) -- 2.45.2 From e228f241bfb2a725dceddba02222d23d0d5d46f4 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 4 Jun 2025 13:29:35 +0200 Subject: [PATCH 11/37] fix initialization of core handler --- PAM/ssh/bin/rublon_application.cpp | 3 ++- PAM/ssh/include/rublon/configuration.hpp | 2 +- PAM/ssh/include/rublon/session.hpp | 18 ++++-------------- PAM/ssh/lib/pam.cpp | 2 +- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/PAM/ssh/bin/rublon_application.cpp b/PAM/ssh/bin/rublon_application.cpp index 8dfde4a..882f80f 100644 --- a/PAM/ssh/bin/rublon_application.cpp +++ b/PAM/ssh/bin/rublon_application.cpp @@ -1,3 +1,4 @@ +#include "rublon/bits.hpp" #include #include #include @@ -46,7 +47,7 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { g_level = LogLevel::Warning; } - auto & CH = session.coreHandler(); + CoreHandler_t CH{session.config()}; auto selectMethod = [&](const MethodSelect & selector) { // return selector.create(pam); diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 6c9765f..a987bc7 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -24,7 +24,7 @@ class Configuration { std::pmr::memory_resource * memoryResource; public: - Configuration(std::pmr::memory_resource * mr = memory::default_resource()) : memoryResource{mr} {} + Configuration(std::pmr::memory_resource * mr) : memoryResource{mr} {} // change to StaticString std::pmr::string systemToken{memoryResource}; diff --git a/PAM/ssh/include/rublon/session.hpp b/PAM/ssh/include/rublon/session.hpp index 21bae4c..58a9e28 100644 --- a/PAM/ssh/include/rublon/session.hpp +++ b/PAM/ssh/include/rublon/session.hpp @@ -1,6 +1,7 @@ #pragma once #include "rublon/memory.hpp" +#include #include #include #include @@ -94,18 +95,15 @@ class DefaultResource { } }; -class Session :public DefaultResource { +class Session : public DefaultResource { std::pmr::memory_resource * mr; const Pam_t & _pam; Configuration _config; - std::pmr::string _tid; std::pmr::string _accessToken; - CoreHandler_t _coreHandler; - /// TODO log public: - Session(const Pam_t & pam) : DefaultResource{}, mr{memory::default_resource()}, _pam{pam}, _config{mr}, _coreHandler{_config} { + Session(const Pam_t & pam) : DefaultResource{}, mr{memory::default_resource()}, _pam{pam}, _config{mr}, _tid{mr}, _accessToken{mr} { details::initLog(); } @@ -115,9 +113,6 @@ class Session :public DefaultResource { Session & operator=(Session &&) noexcept = delete; Session & operator=(const Session &) = delete; - const auto & coreHandler() const { - return _coreHandler; - } const auto & pam() const { return _pam; } @@ -167,12 +162,7 @@ class Session :public DefaultResource { } } const char * ctransactionID() const { - if(_tid.empty()) { - log(LogLevel::Warning, "Transaction ID is not defined, but requested"); - return ""; - } else { - return _tid.data(); - } + return transactionID().data(); } }; diff --git a/PAM/ssh/lib/pam.cpp b/PAM/ssh/lib/pam.cpp index 3f29e20..5bc0ffa 100644 --- a/PAM/ssh/lib/pam.cpp +++ b/PAM/ssh/lib/pam.cpp @@ -68,7 +68,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu g_level = LogLevel::Warning; } - auto & CH = session.coreHandler(); + CoreHandler_t CH{session.config()}; auto selectMethod = [&](const MethodSelect & selector) { // return selector.create(pam); -- 2.45.2 From 18846bf95f2c2f1da5ec3e1f362f4ed8e2efa8d0 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 4 Jun 2025 13:56:54 +0200 Subject: [PATCH 12/37] change logs --- PAM/ssh/include/rublon/init.hpp | 3 ++- PAM/ssh/include/rublon/websockets.hpp | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/PAM/ssh/include/rublon/init.hpp b/PAM/ssh/include/rublon/init.hpp index a0aef2a..e6dbcb3 100644 --- a/PAM/ssh/include/rublon/init.hpp +++ b/PAM/ssh/include/rublon/init.hpp @@ -79,14 +79,15 @@ class Init : public AuthenticationStep { if(rublonStatus) { const auto & status = rublonStatus->GetString(); - log(LogLevel::Warning, "Got enrolement message with stats %s", status); if(status == "pending"sv) { if(rublonWebURI) { pam.print("Visit %s", rublonWebURI->GetString()); } } else if(status == "waiting"sv) { + log(LogLevel::Warning, "Got enrolement message with stats %s", status); return tl::unexpected{Error{RublonAuthenticationInterrupt{RublonAuthenticationInterrupt::UserWaiting}}}; } else if(status == "denied"sv) { + log(LogLevel::Warning, "Got enrolement message with stats %s", status); return tl::unexpected{Error{RublonAuthenticationInterrupt{RublonAuthenticationInterrupt::UserDenied}}}; } } diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index 017477f..f2bef57 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -116,7 +116,7 @@ class WebSocket { ccinfo.pwsi = &wsi; ccinfo.userdata = this; - log(LogLevel::Debug, "Created WS from %s", urlv.data()); + log(LogLevel::Debug, "WebSocket Created connection to %s", urlv.data()); } WebSocket(WebSocket && rhs) noexcept = default; @@ -140,20 +140,20 @@ class WebSocket { memcpy(buf + LWS_PRE, subscribe_message.data(), subscribe_message.size()); - log(LogLevel::Debug, "WS send message: %s", subscribe_message.c_str()); + log(LogLevel::Debug, "WebSocket send message: %s", subscribe_message.c_str()); int bytes_sent = lws_write(wsi, buf + LWS_PRE, subscribe_message.size(), LWS_WRITE_TEXT); - log(LogLevel::Debug, "WS send: %d bytes", bytes_sent); + log(LogLevel::Debug, "WebSocket send: %d bytes", bytes_sent); if(bytes_sent < ( int ) subscribe_message.size()) { - log(LogLevel::Error, "Failed to send subscribe message"); + log(LogLevel::Error, "WebSocket failed to send subscribe message"); return false; } return true; } bool AttachToCore(std::string_view tid) { - log(LogLevel::Debug, "connecting to %s", ccinfo.address); + log(LogLevel::Debug, "WebSocket attaching to rublon api at %s", ccinfo.address); lws_client_connect_via_info(&ccinfo); const int seconds = 10; @@ -188,7 +188,7 @@ class WebSocket { const int seconds = 60; auto endtime = std::chrono::steady_clock::now() + std::chrono::seconds{seconds}; - log(LogLevel::Debug, "waiting for events for %d seconds", seconds); + log(LogLevel::Debug, "WebSocket waiting for events for %d seconds", seconds); while(!event_received && std::chrono::steady_clock::now() < endtime) { lws_service(context, 1000); } @@ -214,7 +214,7 @@ class WebSocket { unsigned char buf[64]={}; memcpy(&buf[LWS_PRE], handshake.data(), handshake.size()); lws_write(wsi, &buf[LWS_PRE], handshake.size(), LWS_WRITE_TEXT); - log(LogLevel::Debug, "Sent Socket.IO handshake"); + log(LogLevel::Debug, "WebSocket Sent Socket.IO handshake"); break; } @@ -236,7 +236,7 @@ class WebSocket { /// TODO refactor to separate class if(_this->currentEvent == nullptr) return -1; - log(LogLevel::Debug, "WS got %s", input.data()); + log(LogLevel::Debug, "WebSocket got %s", input.data()); size_t startPos = input.find("[\"") + 2; size_t endPos = input.find("\",", startPos); auto & event = *_this->currentEvent; @@ -267,19 +267,19 @@ class WebSocket { if(token != nullptr) { event.accessToken = token->GetString(); } else { - log(LogLevel::Error, "Response does not contain token"); + log(LogLevel::Error, "WebSocket response does not contain access token"); } _this->event_received = true; } else if(redirectUrl != nullptr) { - log(LogLevel::Info, "Received deny message"); + log(LogLevel::Info, "WebSocket received deny message"); _this->event_received = true; } else { - log(LogLevel::Error, "event data incorrect"); + log(LogLevel::Error, "WebSocket event data incorrect"); return -1; } } else { - log(LogLevel::Debug, "Not an confirmation event"); + log(LogLevel::Debug, "WebSocket Not an confirmation event"); } break; } -- 2.45.2 From 0b4597444caa345b2a6cf9c3adbd849b5e893c7b Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 4 Jun 2025 14:11:36 +0200 Subject: [PATCH 13/37] fix subscribe message reqression --- PAM/ssh/bin/rublon_application.cpp | 3 +-- PAM/ssh/include/rublon/core_handler.hpp | 3 +-- PAM/ssh/include/rublon/websockets.hpp | 2 +- PAM/ssh/lib/pam.cpp | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/PAM/ssh/bin/rublon_application.cpp b/PAM/ssh/bin/rublon_application.cpp index 882f80f..4279918 100644 --- a/PAM/ssh/bin/rublon_application.cpp +++ b/PAM/ssh/bin/rublon_application.cpp @@ -79,8 +79,7 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { { CheckApplication ca; - auto ret = - ca.call(CH, {session.config().systemToken.data(), session.config().systemToken.size()}).or_else(mapError); + auto ret = ca.call(CH, session.config().systemToken).or_else(mapError); if(not ret.has_value()) { log(LogLevel::Error, "Check Application step failed, check configration"); return PAM_MAXTRIES; diff --git a/PAM/ssh/include/rublon/core_handler.hpp b/PAM/ssh/include/rublon/core_handler.hpp index d252949..4cda5d4 100755 --- a/PAM/ssh/include/rublon/core_handler.hpp +++ b/PAM/ssh/include/rublon/core_handler.hpp @@ -30,8 +30,7 @@ class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { } void signRequest(Request & request) const { - request.headers["X-Rublon-Signature"] = - std::pmr::string{signData(request.body, config().secretKey).c_str(), request.headers.get_allocator()}; + request.headers["X-Rublon-Signature"] = signData(request.body, config().secretKey).c_str(); } bool hasSignature(const Response & response) const { diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index f2bef57..0ea6d72 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -136,7 +136,7 @@ class WebSocket { subscribe_message += R"msg(42["subscribe",{"channel":"transactionConfirmation.)msg"; subscribe_message += transaction_id.data(); - subscribe_message += "}]"; + subscribe_message += R"(")}])"; memcpy(buf + LWS_PRE, subscribe_message.data(), subscribe_message.size()); diff --git a/PAM/ssh/lib/pam.cpp b/PAM/ssh/lib/pam.cpp index 5bc0ffa..1276387 100644 --- a/PAM/ssh/lib/pam.cpp +++ b/PAM/ssh/lib/pam.cpp @@ -99,8 +99,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu { CheckApplication ca; - const auto & config = session.config(); - const auto ret = ca.call(CH, config.systemToken).or_else(mapError); + const auto ret = ca.call(CH, session.config().systemToken).or_else(mapError); if(not ret.has_value()) { log(LogLevel::Error, "Check Application step failed, check configration"); return PAM_MAXTRIES; -- 2.45.2 From 3aca263a53aca57e0267703d6834af47bce755e2 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 4 Jun 2025 14:19:14 +0200 Subject: [PATCH 14/37] --amend --- PAM/ssh/include/rublon/websockets.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index 0ea6d72..a95d3e2 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -136,7 +136,7 @@ class WebSocket { subscribe_message += R"msg(42["subscribe",{"channel":"transactionConfirmation.)msg"; subscribe_message += transaction_id.data(); - subscribe_message += R"(")}])"; + subscribe_message += R"("}])"; memcpy(buf + LWS_PRE, subscribe_message.data(), subscribe_message.size()); -- 2.45.2 From d423ac93e05cdc870dabb4f6b0a05813787afc1f Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Thu, 5 Jun 2025 11:32:30 +0200 Subject: [PATCH 15/37] Add logs to check is proxy is used --- PAM/ssh/include/rublon/curl.hpp | 4 +++- PAM/ssh/include/rublon/websockets.hpp | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index eb020a3..8ace39f 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -23,7 +23,7 @@ namespace { struct Request { std::pmr::memory_resource * _mr; - + std::pmr::map< std::pmr::string, std::pmr::string > headers; std::pmr::string body; @@ -90,6 +90,7 @@ class CURL { // configuration reader check if proxy has needed fields assert(conf().proxyType.has_value()); assert(conf().proxyServer.has_value()); + log(LogLevel::Debug, "CURL using proxy"); std::pmr::string proxyUrl{&memoryResource}; proxyUrl.reserve(conservative_estimate(conf().proxyType, conf().proxyServer, conf().proxyPort) + 10); @@ -103,6 +104,7 @@ class CURL { proxyUrl += std::to_string(*conf().proxyPort); } + log(LogLevel::Debug, "CURL using %s", proxyUrl.c_str()); curl_easy_setopt(curl.get(), CURLOPT_PROXY, proxyUrl.c_str()); if(conf().proxyType == "socks4") { diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index a95d3e2..c2807ad 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -1,21 +1,20 @@ #pragma once -#include "rublon/configuration.hpp" -#include "rublon/json.hpp" -#include "rublon/memory.hpp" -#include "rublon/static_string.hpp" -#include #include #include #include #include -#include -#include -#include - #include #include +#include +#include +#include +#include +#include +#include +#include + #include #include @@ -77,7 +76,8 @@ class WebSocket { if(cfg.proxyEnabled && (cfg.proxyType == "http" || cfg.proxyType == "https")) { assert(cfg.proxyType.has_value()); assert(cfg.proxyServer.has_value()); - + log(LogLevel::Debug, "WebSocket using proxy"); + memory::Monotonic_8k_Resource memoryResource; std::pmr::string proxyUrl{&memoryResource}; proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyServer, cfg.proxyPort) + 10); @@ -99,6 +99,8 @@ class WebSocket { } // Set environment variable for libwebsockets to pick up + + log(LogLevel::Debug, "WebSocket proxy %s", proxyUrl.c_str()); setenv((cfg.proxyType == "https" ? "https_proxy" : "http_proxy"), proxyUrl.c_str(), 1); } @@ -211,7 +213,7 @@ class WebSocket { case LWS_CALLBACK_CLIENT_WRITEABLE: { // Perform the Socket.IO 4.x handshake (send `40` message) const std::string_view handshake = "40"; - unsigned char buf[64]={}; + unsigned char buf[64] = {}; memcpy(&buf[LWS_PRE], handshake.data(), handshake.size()); lws_write(wsi, &buf[LWS_PRE], handshake.size(), LWS_WRITE_TEXT); log(LogLevel::Debug, "WebSocket Sent Socket.IO handshake"); -- 2.45.2 From 4d456c56a878c9934d30313493f1215056b78b5b Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Thu, 5 Jun 2025 13:04:28 +0200 Subject: [PATCH 16/37] Fix http headers bug when using proxy server --- PAM/ssh/include/rublon/utils.hpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index 599f111..685fd2f 100755 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -202,16 +202,21 @@ namespace details { s = s.substr(start, end - start); } } - + template < typename Headers > inline void headers(std::string_view data, Headers & headers) { memory::Monotonic_4k_Resource stackResource; std::pmr::string tmp{&stackResource}; - + tmp.reserve(300); + std::istringstream resp{}; resp.rdbuf()->pubsetbuf(const_cast< char * >(data.data()), data.size()); - - while(std::getline(resp, tmp) && !(trim(tmp).empty())) { + + while(std::getline(resp, tmp)) { + if(tmp == "\r") + continue; + if(trim(tmp).empty()) + break; auto line = std::string_view(tmp); auto index = tmp.find(':', 0); if(index != std::string::npos) { -- 2.45.2 From 5d163800c3fe4a71973e1ce93c1a212d6d200049 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Mon, 9 Jun 2025 13:01:08 +0200 Subject: [PATCH 17/37] Add a proxy fallback, and read proxy from env --- PAM/ssh/bin/rublon_application.cpp | 34 +++++----- PAM/ssh/include/rublon/configuration.hpp | 79 +++++++++++++++++++++++- PAM/ssh/include/rublon/curl.hpp | 2 +- PAM/ssh/include/rublon/utils.hpp | 27 ++++---- PAM/ssh/include/rublon/websockets.hpp | 2 +- 5 files changed, 110 insertions(+), 34 deletions(-) diff --git a/PAM/ssh/bin/rublon_application.cpp b/PAM/ssh/bin/rublon_application.cpp index 4279918..ce07310 100644 --- a/PAM/ssh/bin/rublon_application.cpp +++ b/PAM/ssh/bin/rublon_application.cpp @@ -1,4 +1,3 @@ -#include "rublon/bits.hpp" #include #include #include @@ -6,6 +5,7 @@ #include #include +#include #include #include #include @@ -17,50 +17,50 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { using namespace rublon; details::initLog(); PamStub pam{}; - + auto printAuthMessageAndExit = [&](const AuthenticationStatus status) { switch(status.action()) { case AuthenticationStatus::Action::Bypass: pam.print("RUBLON authentication BYPASSED"); return PAM_SUCCESS; - + case AuthenticationStatus::Action::Denied: pam.print("RUBLON authentication FAILED"); return PAM_MAXTRIES; - + case AuthenticationStatus::Action::Confirmed: pam.print("RUBLON authentication SUCCEEDED"); return PAM_SUCCESS; } - + pam.print("RUBLON connector has exited with unknown code, access DENY!\n"); return PAM_MAXTRIES; }; - + Session session{pam}; auto ok = rublon::RublonFactory{}.initializeSession(session); if(not ok.has_value()) { return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass); } - + if(!session.config().logging) { g_level = LogLevel::Warning; } - + CoreHandler_t CH{session.config()}; - + auto selectMethod = [&](const MethodSelect & selector) { // return selector.create(pam); }; - + auto confirmMethod = [&](const PostMethod & postMethod) { // return postMethod.handle(CH); }; - + auto confirmCode = [&](const MethodProxy & method) mutable { // return method.fire(session, CH, pam); }; - + auto finalizeTransaction = [&](const AuthenticationStatus & status) mutable -> tl::expected< AuthenticationStatus, Error > { if(status.userAuthorized()) { Finish finish{session, status.accessToken()}; @@ -68,15 +68,15 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { } return status; }; - + auto allowLogin = [&](const AuthenticationStatus & status) -> tl::expected< int, Error > { // return printAuthMessageAndExit(status); }; - + auto mapError = [&](const Error & error) -> tl::expected< int, Error > { return printAuthMessageAndExit(rublon::ErrorHandler{pam, session.config()}.printErrorDetails(error)); }; - + { CheckApplication ca; auto ret = ca.call(CH, session.config().systemToken).or_else(mapError); @@ -85,7 +85,7 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { return PAM_MAXTRIES; } } - + auto ret = Init{session} .handle(CH, pam) // .and_then(selectMethod) @@ -94,6 +94,6 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { .and_then(finalizeTransaction) .and_then(allowLogin) .or_else(mapError); - + return ret.value_or(PAM_MAXTRIES); } diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index a987bc7..4622b9d 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -1,5 +1,6 @@ #pragma once +#include "tl/expected.hpp" #include #include #include @@ -60,7 +61,7 @@ class ConfigurationReader { memory::MonotonicStack_1k_Resource memoryResource; std::ifstream file(filepath.data()); if(not file.good()) - return ; + return; std::pmr::string line{&memoryResource}; line.reserve(300); @@ -74,7 +75,7 @@ class ConfigurationReader { continue; auto posEqual = line.find('='); - std::pmr::string key {line.substr(0, posEqual),&memoryResource}; + std::pmr::string key{line.substr(0, posEqual), &memoryResource}; std::pmr::string value{line.substr(posEqual + 1), &memoryResource}; keyValues[std::move(key)] = std::move(value); @@ -147,6 +148,64 @@ class ConfigurationReader { return std::nullopt; }; + auto parseProxyURL = [&](const char * envValue) -> bool { + // Very simple parser: scheme://[user[:pass]@]host[:port] + std::string_view url = envValue; + + std::string_view scheme{}; + std::string_view auth{}; + std::string_view hostport{}; + + auto scheme_pos = url.find("://"); + if(scheme_pos != std::string_view::npos) { + scheme = url.substr(0, scheme_pos); + url = url.substr(scheme_pos + 3); + } else { + scheme = "http"; // default + } + + auto at_pos = url.find('@'); + if(at_pos != std::string_view::npos) { + auth = url.substr(0, at_pos); + hostport = url.substr(at_pos + 1); + } else { + hostport = url; + } + + std::string_view host = hostport; + std::string_view port_str{}; + auto colon_pos = hostport.rfind(':'); + if(colon_pos != std::string_view::npos && colon_pos < hostport.size() - 1) { + host = hostport.substr(0, colon_pos); + port_str = hostport.substr(colon_pos + 1); + } + + config.proxyEnabled = true; + config.proxyType = scheme; + config.proxyServer = host; + + if(!port_str.empty()) { + config.proxyPort = conv::to_uint32opt(port_str); + if(not config.proxyPort) { + log(LogLevel::Error, "Invalid proxy port in environment variable"); + return false; + } + } + + if(!auth.empty()) { + auto colon = auth.find(':'); + if(colon != std::string_view::npos) { + config.proxyUsername = auth.substr(0, colon); + config.proxyPass = auth.substr(colon + 1); + } else { + config.proxyUsername = auth; + } + config.proxyAuthRequired = true; + } + + return true; + }; + /// NOTE: // getStringOpt can return a valid empty string, for example configuration entry like // option= @@ -183,6 +242,22 @@ class ConfigurationReader { config.proxyEnabled = getBool("proxyEnabled").value_or(false); config.proxyType = getStringOpt("proxyType"); config.proxyServer = getStringOpt("proxyServer"); + + + // Apply fallback if no config is set + if (config.proxyEnabled && (!config.proxyType || config.proxyType->empty()) && (!config.proxyServer || config.proxyServer->empty())) { + log(LogLevel::Info, "Proxy is enabled but no configuration for it is provided, trying to read from env"); + if (const char* https_proxy = std::getenv("https_proxy"); https_proxy && *https_proxy) { + if (parseProxyURL(https_proxy)) { + log(LogLevel::Info, "Loaded proxy config from HTTPS_PROXY"); + } + } else if (const char* http_proxy = std::getenv("http_proxy"); http_proxy && *http_proxy) { + if (parseProxyURL(http_proxy)) { + log(LogLevel::Info, "Loaded proxy config from HTTP_PROXY"); + } + } + } + if(config.proxyEnabled) { if(not config.proxyType or config.proxyType->empty()) { log(LogLevel::Error, "Proxy is enabled but proxy type is not present or empty"); diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index 8ace39f..a6add13 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -99,7 +99,7 @@ class CURL { proxyUrl = *conf().proxyType; proxyUrl += "://"; proxyUrl += *conf().proxyServer; - if(conf().proxyPort > 0) { + if(conf().proxyPort.value_or(0) > 0) { proxyUrl += ":"; proxyUrl += std::to_string(*conf().proxyPort); } diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index 685fd2f..1f36b46 100755 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -149,23 +150,23 @@ namespace conv { std::transform(userinput.cbegin(), userinput.cend(), buf.data(), asciitolower); return strcmp(buf.data(), "true") == 0; } - - inline tl::expected< std::uint32_t, Error > to_uint32(std::string_view userinput) noexcept { - try { - return std::stoi(userinput.data()); - } catch(const std::invalid_argument & e) { - return tl::make_unexpected(Error::NotANumber); - } catch(const std::out_of_range & e) { - return tl::make_unexpected(Error::OutOfRange); - } - } inline std::optional< std::uint32_t> to_uint32opt(std::string_view userinput) noexcept { - try { - return std::stoi(userinput.data()); - } catch(...) { + int out; + const std::from_chars_result result = std::from_chars(userinput.data(), userinput.data() + userinput.size(), out); + if(result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) { return std::nullopt; } + return out; + } + + inline tl::expected< std::uint32_t, Error > to_uint32(std::string_view userinput) noexcept { + int out; + const std::from_chars_result result = std::from_chars(userinput.data(), userinput.data() + userinput.size(), out); + if(result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) { + return tl::make_unexpected(Error::NotANumber); + } + return out; } } // namespace conv diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index c2807ad..bead9a8 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -93,7 +93,7 @@ class WebSocket { } proxyUrl += *cfg.proxyServer; - if(cfg.proxyPort > 0) { + if(cfg.proxyPort.value_or(0) > 0) { proxyUrl += ":"; proxyUrl += std::to_string(*cfg.proxyPort); } -- 2.45.2 From 29890305c667ca2573b7339ff94fb03a8f55e81b Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Thu, 12 Jun 2025 13:11:44 +0200 Subject: [PATCH 18/37] Add config entry to check application --- PAM/ssh/include/rublon/check_application.hpp | 6 ++++-- PAM/ssh/include/rublon/init.hpp | 2 +- PAM/ssh/include/rublon/session.hpp | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/PAM/ssh/include/rublon/check_application.hpp b/PAM/ssh/include/rublon/check_application.hpp index 6817702..62e4156 100644 --- a/PAM/ssh/include/rublon/check_application.hpp +++ b/PAM/ssh/include/rublon/check_application.hpp @@ -55,6 +55,7 @@ class Status { std::string_view _appTypeKey = "/type"; std::string_view _paramSystemName = "/params/os"; std::string_view _paramSSHDBase = "/params/sshd_config/"; + std::string_view _configBase = "/config/"; public: Status() : _alloc{}, _data{&_alloc}, _statusUpdated{false} { @@ -95,7 +96,7 @@ class Status { void updateSSHDConfig() { using namespace std::string_view_literals; - constexpr auto keys = make_array("authenticationmethods"sv, + constexpr auto keys = make_array< std::string_view >("authenticationmethods"sv, "challengeresponseauthentication"sv, "kbdinteractiveauthentication"sv, "logingracetime"sv, @@ -171,7 +172,7 @@ class CheckApplication { public: tl::expected< int, Error > call(const CoreHandler_t & coreHandler, std::string_view systemToken) const { - memory::MonotonicStack_1k_Resource mr; + memory::MonotonicStack_2k_Resource mr; RapidJSONPMRAlloc alloc{&mr}; constexpr std::string_view api = "/api/app/init"; Status status; @@ -181,6 +182,7 @@ class CheckApplication { status.updateAppVersion(RUBLON_VERSION_STRING); status.updateSystemVersion(details::osName(&mr)); status.updateSSHDConfig(); + status.updateRublonConfig(); if(status.updated()) { auto & alloc = status.data().GetAllocator(); diff --git a/PAM/ssh/include/rublon/init.hpp b/PAM/ssh/include/rublon/init.hpp index e6dbcb3..ab798e6 100644 --- a/PAM/ssh/include/rublon/init.hpp +++ b/PAM/ssh/include/rublon/init.hpp @@ -60,7 +60,7 @@ class Init : public AuthenticationStep { params.AddMember("appVer", RUBLON_VERSION_STRING, alloc); if(not host.empty()) params.AddMember("hostName", Value{os.c_str(), static_cast< unsigned >(host.size()), alloc}, alloc); - if(not _session.inInteractiveMode()) + if(_session.inNonInteractiveMode()) params.AddMember("mode", "noninteractive", alloc); params.AddMember("os", Value{os.c_str(), static_cast< unsigned >(os.size()), alloc}, alloc); params.AddMember("userIP", Value{pam.ip().get(), alloc}, alloc); diff --git a/PAM/ssh/include/rublon/session.hpp b/PAM/ssh/include/rublon/session.hpp index 58a9e28..25dfa7f 100644 --- a/PAM/ssh/include/rublon/session.hpp +++ b/PAM/ssh/include/rublon/session.hpp @@ -130,7 +130,11 @@ class Session : public DefaultResource { return systemToken().data(); } - bool inInteractiveMode() const { + constexpr bool inNonInteractiveMode() const { + return not inInteractiveMode(); + } + + constexpr bool inInteractiveMode() const { return _config.nonInteractiveMode == false; } -- 2.45.2 From 6f3da4edb6a4a1134be5d27a11d128474793f8f2 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 13 Jun 2025 12:51:58 +0200 Subject: [PATCH 19/37] remove codeconv usage --- PAM/ssh/include/rublon/check_application.hpp | 2 +- PAM/ssh/include/rublon/configuration.hpp | 38 ++++++---- PAM/ssh/include/rublon/utils.hpp | 76 ++++++++++++-------- 3 files changed, 69 insertions(+), 47 deletions(-) diff --git a/PAM/ssh/include/rublon/check_application.hpp b/PAM/ssh/include/rublon/check_application.hpp index 62e4156..63560c7 100644 --- a/PAM/ssh/include/rublon/check_application.hpp +++ b/PAM/ssh/include/rublon/check_application.hpp @@ -182,7 +182,7 @@ class CheckApplication { status.updateAppVersion(RUBLON_VERSION_STRING); status.updateSystemVersion(details::osName(&mr)); status.updateSSHDConfig(); - status.updateRublonConfig(); + // status.updateRublonConfig(); if(status.updated()) { auto & alloc = status.data().GetAllocator(); diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 4622b9d..7d8ec7c 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -33,7 +33,7 @@ class Configuration { std::pmr::string apiServer{memoryResource}; int prompt{}; - bool enablePasswdEmail{}; + bool enablePasswdEmail{}; // obsolete bool logging{}; bool autopushPrompt{}; FailMode failMode{}; @@ -86,6 +86,8 @@ class ConfigurationReader { tl::expected< bool, ConfigurationError > applyTo(Configuration & config) const { using string = std::pmr::string; + auto logRequiredFieldNotAvailable = [](auto fieldname) { log(LogLevel::Error, "%s field is not set", fieldname); }; + // Helper lambdas for conversion auto getStringOpt = [&](const string & key) -> std::optional< std::pmr::string > { auto it = keyValues.find(key); @@ -215,20 +217,26 @@ class ConfigurationReader { // * configuration value is empty -> RequiredValueEmpty // Reading required fields - if(auto val = getStringReq("systemToken"); not val.has_value()) + if(auto val = getStringReq("systemToken"); not val.has_value()) { + logRequiredFieldNotAvailable("systemToken"); return tl::unexpected{val.error()}; - else + } else { config.systemToken = std::move(val.value()); + } - if(auto val = getStringReq("secretKey"); not val.has_value()) + if(auto val = getStringReq("secretKey"); not val.has_value()) { + logRequiredFieldNotAvailable("secretKey"); return tl::unexpected{val.error()}; - else + } else { config.secretKey = std::move(val.value()); + } - if(auto val = getStringReq("rublonApiServer"); not val.has_value()) + if(auto val = getStringReq("rublonApiServer"); not val.has_value()) { + logRequiredFieldNotAvailable("rublonApiServer"); return tl::unexpected{val.error()}; - else + } else { config.apiServer = std::move(val.value()); + } // optional configuration options config.prompt = getInt("prompt").value_or(1); @@ -242,22 +250,22 @@ class ConfigurationReader { config.proxyEnabled = getBool("proxyEnabled").value_or(false); config.proxyType = getStringOpt("proxyType"); config.proxyServer = getStringOpt("proxyServer"); - - + // Apply fallback if no config is set - if (config.proxyEnabled && (!config.proxyType || config.proxyType->empty()) && (!config.proxyServer || config.proxyServer->empty())) { + if(config.proxyEnabled && (!config.proxyType || config.proxyType->empty()) && + (!config.proxyServer || config.proxyServer->empty())) { log(LogLevel::Info, "Proxy is enabled but no configuration for it is provided, trying to read from env"); - if (const char* https_proxy = std::getenv("https_proxy"); https_proxy && *https_proxy) { - if (parseProxyURL(https_proxy)) { + if(const char * https_proxy = std::getenv("https_proxy"); https_proxy && *https_proxy) { + if(parseProxyURL(https_proxy)) { log(LogLevel::Info, "Loaded proxy config from HTTPS_PROXY"); } - } else if (const char* http_proxy = std::getenv("http_proxy"); http_proxy && *http_proxy) { - if (parseProxyURL(http_proxy)) { + } else if(const char * http_proxy = std::getenv("http_proxy"); http_proxy && *http_proxy) { + if(parseProxyURL(http_proxy)) { log(LogLevel::Info, "Loaded proxy config from HTTP_PROXY"); } } } - + if(config.proxyEnabled) { if(not config.proxyType or config.proxyType->empty()) { log(LogLevel::Error, "Proxy is enabled but proxy type is not present or empty"); diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index 1f36b46..54dce12 100755 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -1,17 +1,20 @@ #pragma once -#include +#include +#include +#include #include +#include +#include + #include #include #include #include -#include #include #include #include -#include #include #include @@ -150,23 +153,33 @@ namespace conv { std::transform(userinput.cbegin(), userinput.cend(), buf.data(), asciitolower); return strcmp(buf.data(), "true") == 0; } - - inline std::optional< std::uint32_t> to_uint32opt(std::string_view userinput) noexcept { - int out; - const std::from_chars_result result = std::from_chars(userinput.data(), userinput.data() + userinput.size(), out); - if(result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) { + + inline std::optional< std::uint32_t > to_uint32opt(std::string_view userinput) noexcept { + constexpr auto max = std::numeric_limits< uint32_t >::digits10 + 1; + if(userinput.empty() || userinput.size() >= max) + return std::nullopt; // Avoid large or empty inputs + + char buffer[max]={0}; + std::memcpy(buffer, userinput.data(), userinput.size()); + buffer[userinput.size()] = '\0'; // Ensure null termination + + char * endptr = nullptr; + errno = 0; + + long result = std::strtol(buffer, &endptr, 10); + + if(errno == ERANGE || endptr != buffer + userinput.size() || result < 0 || result > std::numeric_limits::max()) { return std::nullopt; } - return out; + + return static_cast< std::uint32_t >(result); } - + inline tl::expected< std::uint32_t, Error > to_uint32(std::string_view userinput) noexcept { - int out; - const std::from_chars_result result = std::from_chars(userinput.data(), userinput.data() + userinput.size(), out); - if(result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) { - return tl::make_unexpected(Error::NotANumber); - } - return out; + auto val = to_uint32opt(userinput); + if(val) + return *val; + return tl::unexpected{Error::NotANumber}; } } // namespace conv @@ -186,33 +199,33 @@ namespace details { static inline std::string_view trim(std::string_view s) { return ltrim(rtrim(s)); } - - template + + template < typename StrT > void trimInPlace(StrT & s) { // Remove leading whitespace size_t start = 0; while(start < s.size() && isspace(static_cast< unsigned char >(s[start]))) ++start; - - // Remove trailing whitespace + + // Remove trailing whitespace size_t end = s.size(); while(end > start && isspace(static_cast< unsigned char >(s[end - 1]))) --end; - + if(start > 0 || end < s.size()) { s = s.substr(start, end - start); } } - + template < typename Headers > inline void headers(std::string_view data, Headers & headers) { memory::Monotonic_4k_Resource stackResource; std::pmr::string tmp{&stackResource}; tmp.reserve(300); - + std::istringstream resp{}; resp.rdbuf()->pubsetbuf(const_cast< char * >(data.data()), data.size()); - + while(std::getline(resp, tmp)) { if(tmp == "\r") continue; @@ -250,9 +263,8 @@ namespace details { std::pmr::string _key{&memoryResource}; std::pmr::string _value{&memoryResource}; line.reserve(100); - - while(std::getline(file, line)) { + while(std::getline(file, line)) { if(!line.length()) continue; @@ -299,19 +311,21 @@ constexpr std::array< Out, sizeof...(Types) > make_array(Types... names) { return {std::forward< Types >(names)...}; } -template std::size_t size_buffer(const T &item) { - using U = std::decay_t; - if constexpr (std::is_same_v) { +template < typename T > +std::size_t size_buffer(const T & item) { + using U = std::decay_t< T >; + if constexpr(std::is_same_v< U, const char * >) { return strlen(item); } else if constexpr(std::is_same_v< U, std::pmr::string > || std::is_same_v< U, std::string >) { return item.size(); } else if constexpr(std::is_integral_v< U > || std::is_floating_point_v< U >) { - return std::numeric_limits::digits; + return std::numeric_limits< U >::digits; } return 0; } -template std::size_t size_buffer(const std::optional &item) { +template < typename T > +std::size_t size_buffer(const std::optional< T > & item) { if(item.has_value()) return size_buffer(*item); return 0; -- 2.45.2 From ec3d7233b1b35b4029524acbb37080ee9fdfb5bb Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 13 Jun 2025 16:24:52 +0200 Subject: [PATCH 20/37] try to fix compile errors on sles --- PAM/ssh/include/rublon/utils.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index 54dce12..ea98f3c 100755 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -1,15 +1,15 @@ #pragma once -#include +#include +#include +#include + #include #include #include #include #include - -#include -#include -#include +#include #include #include -- 2.45.2 From 58526ee41ccfd7e0cc6a8e06172cdc4e715cca6a Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 13 Jun 2025 16:39:15 +0200 Subject: [PATCH 21/37] Cleanup includes --- PAM/ssh/include/rublon/configuration.hpp | 7 ------- PAM/ssh/include/rublon/core_handler.hpp | 6 ------ PAM/ssh/include/rublon/curl.hpp | 3 --- PAM/ssh/include/rublon/error.hpp | 1 - PAM/ssh/include/rublon/init.hpp | 10 +++------- PAM/ssh/include/rublon/pam_action.hpp | 3 +-- PAM/ssh/include/rublon/session.hpp | 8 ++------ PAM/ssh/include/rublon/utils.hpp | 1 - 8 files changed, 6 insertions(+), 33 deletions(-) diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 7d8ec7c..3caceef 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -1,20 +1,13 @@ #pragma once -#include "tl/expected.hpp" #include #include #include #include #include -#include -#include #include -#include -#include -#include - namespace rublon { class ConfigurationFactory; diff --git a/PAM/ssh/include/rublon/core_handler.hpp b/PAM/ssh/include/rublon/core_handler.hpp index 4cda5d4..cf74d6f 100755 --- a/PAM/ssh/include/rublon/core_handler.hpp +++ b/PAM/ssh/include/rublon/core_handler.hpp @@ -1,10 +1,5 @@ #pragma once -#include "rublon/error.hpp" -#include "rublon/static_string.hpp" -#include -#include -#include #include #include #include @@ -14,7 +9,6 @@ #include #include -#include #include namespace rublon { diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index a6add13..09cc2c7 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -1,8 +1,5 @@ #pragma once -#include "rublon/memory.hpp" -#include -#include #include #include #include diff --git a/PAM/ssh/include/rublon/error.hpp b/PAM/ssh/include/rublon/error.hpp index e92bf9d..dd85c40 100755 --- a/PAM/ssh/include/rublon/error.hpp +++ b/PAM/ssh/include/rublon/error.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include diff --git a/PAM/ssh/include/rublon/init.hpp b/PAM/ssh/include/rublon/init.hpp index ab798e6..65b7343 100644 --- a/PAM/ssh/include/rublon/init.hpp +++ b/PAM/ssh/include/rublon/init.hpp @@ -1,14 +1,10 @@ #pragma once -#include "rublon/memory.hpp" -#include "rublon/utils.hpp" -#include - #include +#include #include #include #include - #include #include @@ -26,7 +22,7 @@ class Init : public AuthenticationStep { RapidJSONPMRAlloc alloc{&stackResource}; const auto * rublonMethods = JSONPointer{"/result/methods", &alloc}.Get(coreResponse); - const auto * rublonTid = JSONPointer{"/result/tid", &alloc}.Get(coreResponse); + const auto * rublonTid = JSONPointer{"/result/tid", &alloc}.Get(coreResponse); if(not rublonMethods) log(LogLevel::Error, "core response has no methods"); @@ -47,7 +43,7 @@ class Init : public AuthenticationStep { memory::MonotonicStackResource< 2_kB > memoryResource; auto & alloc = coreRequest.GetAllocator(); - const auto os = details::osName(&memoryResource); + const auto os = details::osName(&memoryResource); const auto host = details::hostname(&memoryResource); if(os == "unknown") { diff --git a/PAM/ssh/include/rublon/pam_action.hpp b/PAM/ssh/include/rublon/pam_action.hpp index c7aa97f..62ddcda 100644 --- a/PAM/ssh/include/rublon/pam_action.hpp +++ b/PAM/ssh/include/rublon/pam_action.hpp @@ -1,8 +1,7 @@ #pragma once -#include "rublon/static_string.hpp" -#include #include +#include namespace rublon { diff --git a/PAM/ssh/include/rublon/session.hpp b/PAM/ssh/include/rublon/session.hpp index 25dfa7f..cf7d4f0 100644 --- a/PAM/ssh/include/rublon/session.hpp +++ b/PAM/ssh/include/rublon/session.hpp @@ -1,15 +1,11 @@ #pragma once -#include "rublon/memory.hpp" -#include -#include -#include +#include #include #include #include #include -#include -#include +#include namespace rublon { diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index ea98f3c..d846fe0 100755 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include -- 2.45.2 From d2351338e4dde7a59504cc1a486accffc03364e6 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 13 Jun 2025 16:42:09 +0200 Subject: [PATCH 22/37] add experimental unordered map --- PAM/ssh/include/rublon/std_experimental.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/PAM/ssh/include/rublon/std_experimental.hpp b/PAM/ssh/include/rublon/std_experimental.hpp index 949bc0c..0716325 100644 --- a/PAM/ssh/include/rublon/std_experimental.hpp +++ b/PAM/ssh/include/rublon/std_experimental.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include -- 2.45.2 From 9f7cc1bebe4d12754b74a4de83d7dce9c8c2e982 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Fri, 13 Jun 2025 16:44:46 +0200 Subject: [PATCH 23/37] Do not use unordered_map --- PAM/ssh/include/rublon/configuration.hpp | 2 +- PAM/ssh/include/rublon/std_experimental.hpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 3caceef..ea23bb3 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -291,7 +291,7 @@ class ConfigurationReader { private: std::pmr::memory_resource * memoryResource; - std::pmr::unordered_map< std::pmr::string, std::pmr::string > keyValues{memoryResource}; + std::pmr::map< std::pmr::string, std::pmr::string > keyValues{memoryResource}; }; } // namespace rublon diff --git a/PAM/ssh/include/rublon/std_experimental.hpp b/PAM/ssh/include/rublon/std_experimental.hpp index 0716325..949bc0c 100644 --- a/PAM/ssh/include/rublon/std_experimental.hpp +++ b/PAM/ssh/include/rublon/std_experimental.hpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include -- 2.45.2 From b03fe57d3c6246337e235fdde9c5be4df3900eaf Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Mon, 16 Jun 2025 12:52:25 +0200 Subject: [PATCH 24/37] remove usage of unordered_map --- PAM/ssh/include/rublon/configuration.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index ea23bb3..240a8e3 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -44,7 +44,6 @@ class Configuration { class ConfigurationReader { public: ConfigurationReader(std::pmr::memory_resource * memResource, std::string_view filepath) : memoryResource(memResource) { - keyValues.reserve(20); loadFromFile(filepath); } -- 2.45.2 From 93b34d46a98b003de17382d2644eb0c9913fbd28 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Mon, 16 Jun 2025 14:05:34 +0200 Subject: [PATCH 25/37] add configuration dump to check application --- PAM/ssh/bin/rublon_application.cpp | 2 +- PAM/ssh/include/rublon/check_application.hpp | 88 +++++++++++++++++++- PAM/ssh/lib/pam.cpp | 2 +- 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/PAM/ssh/bin/rublon_application.cpp b/PAM/ssh/bin/rublon_application.cpp index ce07310..3912ccb 100644 --- a/PAM/ssh/bin/rublon_application.cpp +++ b/PAM/ssh/bin/rublon_application.cpp @@ -78,7 +78,7 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { }; { - CheckApplication ca; + CheckApplication ca{session}; auto ret = ca.call(CH, session.config().systemToken).or_else(mapError); if(not ret.has_value()) { log(LogLevel::Error, "Check Application step failed, check configration"); diff --git a/PAM/ssh/include/rublon/check_application.hpp b/PAM/ssh/include/rublon/check_application.hpp index 63560c7..3ec04ef 100644 --- a/PAM/ssh/include/rublon/check_application.hpp +++ b/PAM/ssh/include/rublon/check_application.hpp @@ -1,5 +1,7 @@ #pragma once +#include "rublon/configuration.hpp" +#include "rublon/session.hpp" #include #include #include @@ -11,6 +13,8 @@ #include #include +#include +#include namespace rublon { @@ -127,6 +131,85 @@ class Status { } } + template < typename T > + void updateRublonConfigParameter(std::string_view name, const T & newParam) { + memory::MonotonicStack_1k_Resource memoryResource{}; + RapidJSONPMRAlloc stackAlloc{&memoryResource}; + + std::pmr::string jsonPath{&memoryResource}; + jsonPath += _configBase; + jsonPath += name; + + JSONPointer jsonPointer{jsonPath.c_str(), &stackAlloc}; + auto * param = jsonPointer.Get(_data); + + Document::AllocatorType & alloc = _data.GetAllocator(); + Value newValue; + + if constexpr(std::is_same_v< T, std::pmr::string > || std::is_same_v< T, std::string_view >) { + newValue.SetString(newParam.data(), static_cast< rapidjson::SizeType >(newParam.size()), alloc); + if(!param || !param->IsString() || param->GetString() != newParam) { + markUpdated(); + jsonPointer.Set(_data, newValue); + } + } else if constexpr(std::is_same_v< T, bool >) { + newValue.SetBool(newParam); + if(!param || !param->IsBool() || param->GetBool() != newParam) { + markUpdated(); + jsonPointer.Set(_data, newValue); + } + } else if constexpr(std::is_integral_v< T >) { + newValue.SetInt(static_cast< int >(newParam)); + if(!param || !param->IsInt() || param->GetInt() != static_cast< int >(newParam)) { + markUpdated(); + jsonPointer.Set(_data, newValue); + } + } + } + + // For std::optional + template < typename T > + void updateRublonConfigParameter(std::string_view name, const std::optional< T > & optParam) { + memory::MonotonicStack_1k_Resource memoryResource{}; + RapidJSONPMRAlloc stackAlloc{&memoryResource}; + + std::pmr::string fullPath{&memoryResource}; + fullPath += _configBase; + fullPath += name; + + JSONPointer jsonPointer{fullPath.c_str(), &stackAlloc}; + Value* existing = jsonPointer.Get(_data); + + if (optParam.has_value()) { + // Delegate to regular (non-optional) setter + updateRublonConfigParameter(name, *optParam); + } else { + // Set the entire field to `null` if no value + if (!existing || !existing->IsNull()) { + markUpdated(); + Value nullValue(rapidjson::kNullType); + jsonPointer.Set(_data, nullValue); + } + } + } + + void updateRublonConfig(const Configuration & config) { + updateRublonConfigParameter("prompt", config.prompt); + updateRublonConfigParameter("logging", config.logging); + updateRublonConfigParameter("autopushPrompt", config.autopushPrompt); + updateRublonConfigParameter("failMode", static_cast(config.failMode)); + updateRublonConfigParameter("nonInteractiveMode", config.nonInteractiveMode); + + updateRublonConfigParameter("proxyType", config.proxyType); + updateRublonConfigParameter("proxyServer", config.proxyServer); + updateRublonConfigParameter("proxyUsername", config.proxyUsername); + updateRublonConfigParameter("proxyPass", config.proxyPass); + updateRublonConfigParameter("proxyPort", config.proxyPort); + + updateRublonConfigParameter("proxyAuthRequired", config.proxyAuthRequired); + updateRublonConfigParameter("proxyEnabled", config.proxyEnabled); + } + void markUpdated() { _statusUpdated = true; } @@ -164,6 +247,7 @@ class Status { }; class CheckApplication { + const Session & _sesion; tl::expected< bool, Error > persistStatus(Status & status) const { status.data().RemoveMember("systemToken"); status.save(); @@ -171,6 +255,8 @@ class CheckApplication { } public: + CheckApplication(const Session & session) : _sesion{session} {} + tl::expected< int, Error > call(const CoreHandler_t & coreHandler, std::string_view systemToken) const { memory::MonotonicStack_2k_Resource mr; RapidJSONPMRAlloc alloc{&mr}; @@ -182,7 +268,7 @@ class CheckApplication { status.updateAppVersion(RUBLON_VERSION_STRING); status.updateSystemVersion(details::osName(&mr)); status.updateSSHDConfig(); - // status.updateRublonConfig(); + status.updateRublonConfig(_sesion.config()); if(status.updated()) { auto & alloc = status.data().GetAllocator(); diff --git a/PAM/ssh/lib/pam.cpp b/PAM/ssh/lib/pam.cpp index 1276387..06548c2 100644 --- a/PAM/ssh/lib/pam.cpp +++ b/PAM/ssh/lib/pam.cpp @@ -98,7 +98,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu }; { - CheckApplication ca; + CheckApplication ca{session}; const auto ret = ca.call(CH, session.config().systemToken).or_else(mapError); if(not ret.has_value()) { log(LogLevel::Error, "Check Application step failed, check configration"); -- 2.45.2 From 2012f6a7bc7a3de14a669d0f7cf2d0fdbd13ecfb Mon Sep 17 00:00:00 2001 From: madzik Date: Tue, 17 Jun 2025 11:26:50 +0000 Subject: [PATCH 26/37] Update rhel8 packages --- os/rhel/8/Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/os/rhel/8/Dockerfile b/os/rhel/8/Dockerfile index 14377c8..ce4d66a 100644 --- a/os/rhel/8/Dockerfile +++ b/os/rhel/8/Dockerfile @@ -8,7 +8,8 @@ RUN yum update -y && yum install -y gcc \ rpm-build \ openssh-server \ gcc-c++ \ - wget -RUN wget https://yum.oracle.com/repo/OracleLinux/OL8/baseos/latest/x86_64/getPackage/pam-devel-1.3.1-34.0.1.el8_10.x86_64.rpm + wget\ + pam +RUN wget ftp://ftp.icm.edu.pl/vol/rzm5/linux-rocky/8.10/BaseOS/x86_64/os/Packages/p/pam-devel-1.3.1-36.el8_10.x86_64.rpm RUN rpm -Uvh pam* -- 2.45.2 From 40e2aa57135053c61d055124ebbca7d756604d98 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Tue, 17 Jun 2025 13:42:10 +0200 Subject: [PATCH 27/37] Update version --- CMakeLists.txt | 30 +++++++++++++++--------------- PAM/ssh/CMakeLists.txt | 10 ++++++---- PAM/ssh/patches/rapidjson.patch | 13 +++++++++++++ 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a511b30..cda36dc 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(CTest) include(GNUInstallDirs) set(PROJECT_VERSION_MAJOR 2) -set(PROJECT_VERSION_MINOR 2) +set(PROJECT_VERSION_MINOR 3) set(PROJECT_VERSION_PATCH 0) set(RUBLON_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") @@ -34,10 +34,10 @@ execute_process ( if ( ${outOS} MATCHES "ubuntu" OR ${outOS} MATCHES "debian" OR ${outOS} MATCHES "FREEBSD" ) install( FILES - ${CMAKE_CURRENT_LIST_DIR}/rsc/rublon.config.defaults + ${CMAKE_CURRENT_LIST_DIR}/rsc/rublon.config.defaults ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh_pubkey.conf.default - ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh.conf.default - ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey.sh + ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh.conf.default + ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey.sh DESTINATION share/rublon COMPONENT @@ -50,17 +50,17 @@ install( else () install( FILES - ${CMAKE_CURRENT_LIST_DIR}/rsc/rublon.config.defaults - ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh_pubkey.conf.default - ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh.conf.default - ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.mod - ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.pp - ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.te - ${CMAKE_CURRENT_LIST_DIR}/service/pam_service.txt - ${CMAKE_CURRENT_LIST_DIR}/service/rublon_veritas - ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey_rhel_9.sh - ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey_rhel_8.sh - ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey.sh + ${CMAKE_CURRENT_LIST_DIR}/rsc/rublon.config.defaults + ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh_pubkey.conf.default + ${CMAKE_CURRENT_LIST_DIR}/service/01-rublon-ssh.conf.default + ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.mod + ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.pp + ${CMAKE_CURRENT_LIST_DIR}/service/login_rublon.te + ${CMAKE_CURRENT_LIST_DIR}/service/pam_service.txt + ${CMAKE_CURRENT_LIST_DIR}/service/rublon_veritas + ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey_rhel_9.sh + ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey_rhel_8.sh + ${CMAKE_CURRENT_LIST_DIR}/service/inst_pubkey.sh DESTINATION share/rublon COMPONENT diff --git a/PAM/ssh/CMakeLists.txt b/PAM/ssh/CMakeLists.txt index 3f7b726..2b1e996 100755 --- a/PAM/ssh/CMakeLists.txt +++ b/PAM/ssh/CMakeLists.txt @@ -5,11 +5,12 @@ set(INC ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/bits.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/check_application.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/configuration.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/configuration.hpp.orig ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/core_handler.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/core_handler_interface.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/curl.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/error.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/error_handler.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/error.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/finish.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/init.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/json.hpp @@ -18,8 +19,8 @@ set(INC ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/method_select.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/OTP.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/passcode_based_auth.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/PUSH.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/phone_call.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/PUSH.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/SMS.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/SmsLink.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/websocket_based_auth.hpp @@ -27,6 +28,7 @@ set(INC ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/non_owning_ptr.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/pam_action.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/pam.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/pam_stub.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/rublon.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/session.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/sign.hpp @@ -128,7 +130,7 @@ FetchContent_Declare( RapidJSON URL https://github.com/Tencent/rapidjson/archive/refs/tags/v1.1.0.zip URL_HASH MD5=ceb1cf16e693a3170c173dc040a9d2bd - # PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_LIST_DIR}/patches/rapidjson.patch + PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_LIST_DIR}/patches/rapidjson.patch ) if(NOT RapidJSON_POPULATED) @@ -139,6 +141,6 @@ endif() add_subdirectory(lib) add_subdirectory(bin) -# if(${ENABLE_TESTS}) +# if(${ENABLE_TESTS}): # add_subdirectory(tests) # endif() diff --git a/PAM/ssh/patches/rapidjson.patch b/PAM/ssh/patches/rapidjson.patch index e43d863..f9c2055 100644 --- a/PAM/ssh/patches/rapidjson.patch +++ b/PAM/ssh/patches/rapidjson.patch @@ -1,3 +1,16 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index ceda71b..0128f99 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -9,6 +9,7 @@ endif() + + SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules) + ++cmake_policy(SET CMP0048 NEW) + PROJECT(RapidJSON CXX) + + set(LIB_MAJOR_VERSION "1") + diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index e3e20dfb..592c5678 100644 --- a/include/rapidjson/document.h -- 2.45.2 From 5fd06e28756b69b7fda96658c0eec7902cef4bed Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 18 Jun 2025 09:40:06 +0200 Subject: [PATCH 28/37] change proxy configuration parameters names --- PAM/ssh/include/rublon/check_application.hpp | 4 +- PAM/ssh/include/rublon/configuration.hpp | 41 +++++++++++++------- PAM/ssh/include/rublon/curl.hpp | 6 +-- PAM/ssh/include/rublon/websockets.hpp | 6 +-- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/PAM/ssh/include/rublon/check_application.hpp b/PAM/ssh/include/rublon/check_application.hpp index 3ec04ef..31f8b7f 100644 --- a/PAM/ssh/include/rublon/check_application.hpp +++ b/PAM/ssh/include/rublon/check_application.hpp @@ -201,9 +201,9 @@ class Status { updateRublonConfigParameter("nonInteractiveMode", config.nonInteractiveMode); updateRublonConfigParameter("proxyType", config.proxyType); - updateRublonConfigParameter("proxyServer", config.proxyServer); + updateRublonConfigParameter("proxyHost", config.proxyHost); updateRublonConfigParameter("proxyUsername", config.proxyUsername); - updateRublonConfigParameter("proxyPass", config.proxyPass); + updateRublonConfigParameter("proxyPassword", config.proxyPass); updateRublonConfigParameter("proxyPort", config.proxyPort); updateRublonConfigParameter("proxyAuthRequired", config.proxyAuthRequired); diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index 240a8e3..e294adf 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -33,7 +33,7 @@ class Configuration { bool nonInteractiveMode{}; std::optional< std::pmr::string > proxyType{memoryResource}; - std::optional< std::pmr::string > proxyServer{memoryResource}; + std::optional< std::pmr::string > proxyHost{memoryResource}; std::optional< std::pmr::string > proxyUsername{memoryResource}; std::optional< std::pmr::string > proxyPass{memoryResource}; std::optional< int > proxyPort{}; @@ -108,7 +108,7 @@ class ConfigurationReader { }; auto getBool = [&](const string & key) -> std::optional< bool > { - memory::MonotonicStackResource< 64 > memoryResource; + memory::MonotonicStackResource< 32 > memoryResource; auto it = keyValues.find(key); if(it == keyValues.end()) return std::nullopt; @@ -142,10 +142,8 @@ class ConfigurationReader { return std::nullopt; }; - auto parseProxyURL = [&](const char * envValue) -> bool { + auto parseProxyURL = [&](std::string_view url) -> bool { // Very simple parser: scheme://[user[:pass]@]host[:port] - std::string_view url = envValue; - std::string_view scheme{}; std::string_view auth{}; std::string_view hostport{}; @@ -176,7 +174,7 @@ class ConfigurationReader { config.proxyEnabled = true; config.proxyType = scheme; - config.proxyServer = host; + config.proxyHost = host; if(!port_str.empty()) { config.proxyPort = conv::to_uint32opt(port_str); @@ -241,17 +239,16 @@ class ConfigurationReader { // reading proxy configuration config.proxyEnabled = getBool("proxyEnabled").value_or(false); config.proxyType = getStringOpt("proxyType"); - config.proxyServer = getStringOpt("proxyServer"); + config.proxyHost = getStringOpt("proxyHost"); // Apply fallback if no config is set - if(config.proxyEnabled && (!config.proxyType || config.proxyType->empty()) && - (!config.proxyServer || config.proxyServer->empty())) { + if(config.proxyEnabled && (!config.proxyType || config.proxyType->empty()) && (!config.proxyHost || config.proxyHost->empty())) { log(LogLevel::Info, "Proxy is enabled but no configuration for it is provided, trying to read from env"); - if(const char * https_proxy = std::getenv("https_proxy"); https_proxy && *https_proxy) { + if(auto https_proxy = std::getenv("https_proxy"); https_proxy && *https_proxy) { if(parseProxyURL(https_proxy)) { log(LogLevel::Info, "Loaded proxy config from HTTPS_PROXY"); } - } else if(const char * http_proxy = std::getenv("http_proxy"); http_proxy && *http_proxy) { + } else if(auto http_proxy = std::getenv("http_proxy"); http_proxy && *http_proxy) { if(parseProxyURL(http_proxy)) { log(LogLevel::Info, "Loaded proxy config from HTTP_PROXY"); } @@ -263,14 +260,14 @@ class ConfigurationReader { log(LogLevel::Error, "Proxy is enabled but proxy type is not present or empty"); return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration}; } - if(not config.proxyServer or config.proxyServer->empty()) { + if(not config.proxyHost or config.proxyHost->empty()) { log(LogLevel::Error, "Proxy is enabled but proxy server is not present or empty"); return tl::unexpected{ConfigurationError::ErrorClass::BadConfiguration}; } } config.proxyAuthRequired = getBool("proxyAuthRequired").value_or(false); config.proxyUsername = getStringOpt("proxyUsername"); - config.proxyPass = getStringOpt("proxyPass"); + config.proxyPass = getStringOpt("proxyPassword"); if(config.proxyAuthRequired) { if(not config.proxyUsername or config.proxyUsername->empty()) { log(LogLevel::Error, "Proxy auth is required but proxy proxy username is not present or empty"); @@ -284,7 +281,23 @@ class ConfigurationReader { } } - config.proxyPort = getInt("proxyPort").value_or(8080); + auto defaultProxyPort = [&]() -> int { + memory::MonotonicStackResource< 32 > memoryResource; + + if(config.proxyType) { + std::pmr::string val{*config.proxyType, &memoryResource}; + std::transform(val.begin(), val.end(), val.begin(), [](unsigned char c) { return static_cast< char >(std::tolower(c)); }); + if(val == "socks") { + return 1080; + } + } + return 8080; + }; + + if(config.proxyEnabled and not config.proxyPort) { + config.proxyPort = getInt("proxyPort").value_or(defaultProxyPort()); + } + return true; } diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index 09cc2c7..4e7b7fc 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -86,16 +86,16 @@ class CURL { if(conf().proxyEnabled) { // configuration reader check if proxy has needed fields assert(conf().proxyType.has_value()); - assert(conf().proxyServer.has_value()); + assert(conf().proxyHost.has_value()); log(LogLevel::Debug, "CURL using proxy"); std::pmr::string proxyUrl{&memoryResource}; - proxyUrl.reserve(conservative_estimate(conf().proxyType, conf().proxyServer, conf().proxyPort) + 10); + proxyUrl.reserve(conservative_estimate(conf().proxyType, conf().proxyHost, conf().proxyPort) + 10); if(conf().proxyType == "http" || conf().proxyType == "https" || conf().proxyType == "socks4" || conf().proxyType == "socks5") { proxyUrl = *conf().proxyType; proxyUrl += "://"; - proxyUrl += *conf().proxyServer; + proxyUrl += *conf().proxyHost; if(conf().proxyPort.value_or(0) > 0) { proxyUrl += ":"; proxyUrl += std::to_string(*conf().proxyPort); diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index bead9a8..418498a 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -75,12 +75,12 @@ class WebSocket { if(cfg.proxyEnabled && (cfg.proxyType == "http" || cfg.proxyType == "https")) { assert(cfg.proxyType.has_value()); - assert(cfg.proxyServer.has_value()); + assert(cfg.proxyHost.has_value()); log(LogLevel::Debug, "WebSocket using proxy"); memory::Monotonic_8k_Resource memoryResource; std::pmr::string proxyUrl{&memoryResource}; - proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyServer, cfg.proxyPort) + 10); + proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyHost, cfg.proxyPort) + 10); proxyUrl += cfg.proxyType->data(); proxyUrl += "://"; @@ -92,7 +92,7 @@ class WebSocket { proxyUrl += "@"; } - proxyUrl += *cfg.proxyServer; + proxyUrl += *cfg.proxyHost; if(cfg.proxyPort.value_or(0) > 0) { proxyUrl += ":"; proxyUrl += std::to_string(*cfg.proxyPort); -- 2.45.2 From d5462e9b1b6acdcd9b657dda623608a7f55a3d75 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 18 Jun 2025 09:44:03 +0200 Subject: [PATCH 29/37] remove .orig file from sources --- PAM/ssh/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/PAM/ssh/CMakeLists.txt b/PAM/ssh/CMakeLists.txt index 2b1e996..b6eb546 100755 --- a/PAM/ssh/CMakeLists.txt +++ b/PAM/ssh/CMakeLists.txt @@ -5,7 +5,6 @@ set(INC ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/bits.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/check_application.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/configuration.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/configuration.hpp.orig ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/core_handler.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/core_handler_interface.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/curl.hpp -- 2.45.2 From 4cea2022780e156f16e10d0af1750e05350b952a Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 25 Jun 2025 19:00:05 +0200 Subject: [PATCH 30/37] Fix proxy for websockets --- PAM/ssh/include/rublon/configuration.hpp | 4 ++-- PAM/ssh/include/rublon/websockets.hpp | 24 +++++++++++++++--------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/PAM/ssh/include/rublon/configuration.hpp b/PAM/ssh/include/rublon/configuration.hpp index e294adf..0c304f3 100644 --- a/PAM/ssh/include/rublon/configuration.hpp +++ b/PAM/ssh/include/rublon/configuration.hpp @@ -286,8 +286,8 @@ class ConfigurationReader { if(config.proxyType) { std::pmr::string val{*config.proxyType, &memoryResource}; - std::transform(val.begin(), val.end(), val.begin(), [](unsigned char c) { return static_cast< char >(std::tolower(c)); }); - if(val == "socks") { + std::transform(val.begin(), val.end(), val.begin(), [](auto c) { return std::tolower(c); }); + if(val.find("socks") != std::pmr::string::npos) { return 1080; } } diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index 418498a..1e6c70d 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -41,9 +41,17 @@ class WebSocket { lws_client_connect_info ccinfo{}; RublonEventData * currentEvent{nullptr}; + std::pmr::string proxyUrl{}; + +// constexpr static const struct lws_protocol_vhost_options pvo = { +// NULL, /* "next" pvo linked-list / +// &pvo_proxy_uri, / "child" pvo linked-list / +// "push_protocol", / protocol name we belong to on this vhost / +// "" / ignored */ +// }; public: - WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer} { + WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer}, proxyUrl{_config.get().apiServer.get_allocator()} { const auto & cfg = _config.get(); // only a alias to not use _config.get() all the time auto lws_log_emit = [](int level, const char * line) { @@ -77,20 +85,18 @@ class WebSocket { assert(cfg.proxyType.has_value()); assert(cfg.proxyHost.has_value()); log(LogLevel::Debug, "WebSocket using proxy"); - - memory::Monotonic_8k_Resource memoryResource; - std::pmr::string proxyUrl{&memoryResource}; + + // "username:password\@server:port" proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyHost, cfg.proxyPort) + 10); - - proxyUrl += cfg.proxyType->data(); - proxyUrl += "://"; - if(cfg.proxyAuthRequired) { proxyUrl += *cfg.proxyUsername; proxyUrl += ":"; proxyUrl += *cfg.proxyPass; proxyUrl += "@"; } + + proxyUrl += cfg.proxyType->data(); + proxyUrl += "://"; proxyUrl += *cfg.proxyHost; if(cfg.proxyPort.value_or(0) > 0) { @@ -101,7 +107,7 @@ class WebSocket { // Set environment variable for libwebsockets to pick up log(LogLevel::Debug, "WebSocket proxy %s", proxyUrl.c_str()); - setenv((cfg.proxyType == "https" ? "https_proxy" : "http_proxy"), proxyUrl.c_str(), 1); + info.http_proxy_address = proxyUrl.c_str(); } const std::string_view prefix = "https://"; -- 2.45.2 From eb8f00c8eceb95a74a0c7b7cb1e47d7a6cc818d7 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 25 Jun 2025 19:04:41 +0200 Subject: [PATCH 31/37] Fix proxy for websockets --- PAM/ssh/include/rublon/websockets.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index 1e6c70d..5c55842 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -107,7 +107,9 @@ class WebSocket { // Set environment variable for libwebsockets to pick up log(LogLevel::Debug, "WebSocket proxy %s", proxyUrl.c_str()); + info.http_proxy_address = proxyUrl.c_str(); + info.http_proxy_port = config.proxyPort.value_or(8080); } const std::string_view prefix = "https://"; -- 2.45.2 From 677ba438be3ec8aeb28d4273a39c30317a0e1a45 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 25 Jun 2025 19:10:20 +0200 Subject: [PATCH 32/37] Fix proxy for websockets --- PAM/ssh/include/rublon/websockets.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index 5c55842..7e9a051 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -94,9 +94,6 @@ class WebSocket { proxyUrl += *cfg.proxyPass; proxyUrl += "@"; } - - proxyUrl += cfg.proxyType->data(); - proxyUrl += "://"; proxyUrl += *cfg.proxyHost; if(cfg.proxyPort.value_or(0) > 0) { -- 2.45.2 From 6747caa56cdfae5c9740100746b4980150d81263 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 25 Jun 2025 19:13:21 +0200 Subject: [PATCH 33/37] Fix proxy for websockets --- PAM/ssh/include/rublon/websockets.hpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index 7e9a051..d8976a1 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -87,7 +87,7 @@ class WebSocket { log(LogLevel::Debug, "WebSocket using proxy"); // "username:password\@server:port" - proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyHost, cfg.proxyPort) + 10); + proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyHost) + 10); if(cfg.proxyAuthRequired) { proxyUrl += *cfg.proxyUsername; proxyUrl += ":"; @@ -96,15 +96,7 @@ class WebSocket { } proxyUrl += *cfg.proxyHost; - if(cfg.proxyPort.value_or(0) > 0) { - proxyUrl += ":"; - proxyUrl += std::to_string(*cfg.proxyPort); - } - - // Set environment variable for libwebsockets to pick up - log(LogLevel::Debug, "WebSocket proxy %s", proxyUrl.c_str()); - info.http_proxy_address = proxyUrl.c_str(); info.http_proxy_port = config.proxyPort.value_or(8080); } -- 2.45.2 From c1806bc911477ac36dd97812b907af4a562994c9 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 25 Jun 2025 19:16:30 +0200 Subject: [PATCH 34/37] Fix proxy for websockets --- PAM/ssh/include/rublon/websockets.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index d8976a1..6d79364 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -70,7 +70,7 @@ class WebSocket { log(rlevel, "libwesockets: %s", line); }; - lws_set_log_level(LLL_ERR | LLL_WARN, lws_log_emit); + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG | LLL_HEADER, lws_log_emit); memset(&info, 0, sizeof(info)); memset(&ccinfo, 0, sizeof(ccinfo)); -- 2.45.2 From b629f2d7e1ee9060a4eab81093920d445434ccb2 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 25 Jun 2025 19:33:42 +0200 Subject: [PATCH 35/37] Fix proxy for websockets --- PAM/ssh/include/rublon/websockets.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index 6d79364..cd25cae 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -78,9 +78,7 @@ class WebSocket { info.port = CONTEXT_PORT_NO_LISTEN; info.protocols = protocols; info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - - context = lws_create_context(&info); - + if(cfg.proxyEnabled && (cfg.proxyType == "http" || cfg.proxyType == "https")) { assert(cfg.proxyType.has_value()); assert(cfg.proxyHost.has_value()); @@ -100,6 +98,8 @@ class WebSocket { info.http_proxy_address = proxyUrl.c_str(); info.http_proxy_port = config.proxyPort.value_or(8080); } + + context = lws_create_context(&info); const std::string_view prefix = "https://"; if(urlv.substr(0, prefix.size()) == prefix) -- 2.45.2 From b6bf3792d8dbb7b407dbfb6d8acf63ab1f733f15 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 25 Jun 2025 19:44:13 +0200 Subject: [PATCH 36/37] Fix proxy for websockets --- PAM/ssh/include/rublon/websockets.hpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp index cd25cae..47ffbf9 100644 --- a/PAM/ssh/include/rublon/websockets.hpp +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -42,13 +42,6 @@ class WebSocket { RublonEventData * currentEvent{nullptr}; std::pmr::string proxyUrl{}; - -// constexpr static const struct lws_protocol_vhost_options pvo = { -// NULL, /* "next" pvo linked-list / -// &pvo_proxy_uri, / "child" pvo linked-list / -// "push_protocol", / protocol name we belong to on this vhost / -// "" / ignored */ -// }; public: WebSocket(const Configuration & config) : _config{config}, urlv{_config.get().apiServer}, proxyUrl{_config.get().apiServer.get_allocator()} { @@ -70,7 +63,11 @@ class WebSocket { log(rlevel, "libwesockets: %s", line); }; - lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG | LLL_HEADER, lws_log_emit); + if(_config.get().logging) { + lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG | LLL_HEADER, lws_log_emit); + } else { + lws_set_log_level(LLL_ERR | LLL_WARN, lws_log_emit); + } memset(&info, 0, sizeof(info)); memset(&ccinfo, 0, sizeof(ccinfo)); @@ -78,15 +75,15 @@ class WebSocket { info.port = CONTEXT_PORT_NO_LISTEN; info.protocols = protocols; info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - + if(cfg.proxyEnabled && (cfg.proxyType == "http" || cfg.proxyType == "https")) { assert(cfg.proxyType.has_value()); assert(cfg.proxyHost.has_value()); log(LogLevel::Debug, "WebSocket using proxy"); - + // "username:password\@server:port" - proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyHost) + 10); if(cfg.proxyAuthRequired) { + proxyUrl.reserve(conservative_estimate(cfg.proxyUsername, cfg.proxyPass, cfg.proxyHost) + 10); proxyUrl += *cfg.proxyUsername; proxyUrl += ":"; proxyUrl += *cfg.proxyPass; @@ -96,9 +93,9 @@ class WebSocket { proxyUrl += *cfg.proxyHost; log(LogLevel::Debug, "WebSocket proxy %s", proxyUrl.c_str()); info.http_proxy_address = proxyUrl.c_str(); - info.http_proxy_port = config.proxyPort.value_or(8080); + info.http_proxy_port = config.proxyPort.value_or(8080); } - + context = lws_create_context(&info); const std::string_view prefix = "https://"; -- 2.45.2 From fe8e5d0fe99782cc0c82343b5fe2894c9079e118 Mon Sep 17 00:00:00 2001 From: Bartosz Wieczorek Date: Wed, 25 Jun 2025 20:00:13 +0200 Subject: [PATCH 37/37] Fix proxy for websockets --- PAM/ssh/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PAM/ssh/CMakeLists.txt b/PAM/ssh/CMakeLists.txt index b6eb546..d00c296 100755 --- a/PAM/ssh/CMakeLists.txt +++ b/PAM/ssh/CMakeLists.txt @@ -98,7 +98,7 @@ set(LWS_WITH_UPNG OFF) set(LWS_WITH_UDP OFF) set(LWS_WITH_HTTP_STREAM_COMPRESSION OFF) set(LWS_WITH_HTTP_BROTLI OFF) -set(LWS_WITH_ZLIB OFF) +set(LWS_WITH_ZLIB OFF) set(RAPIDJSON_BUILD_DOC OFF CACHE BOOL "" FORCE) set(RAPIDJSON_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) -- 2.45.2