diff --git a/CMakeLists.txt b/CMakeLists.txt index e71b759..6865949 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.5) project(rublon-ssh LANGUAGES CXX) include(CTest) +include(GNUInstallDirs) set(PROJECT_VERSION_MAJOR 0) set(PROJECT_VERSION_MINOR 1) @@ -16,6 +17,21 @@ add_compile_options(-Wall -Wextra -Wpedantic -Wno-format-security) option(ENABLE_TESTS "Enable tests" ON) +add_custom_target(CONFIG_IDE SOURCES ${CMAKE_CURRENT_LIST_DIR}/rsc/rublon.config.defaults) +add_custom_target(INSTSCRIPTS_IDE SUORCES ${CMAKE_CURRENT_LIST_DIR}/service/postinst) + +# TODO configure to fill sysconfdir in postinst +# TODO add postrm that will disable PAM + +install( + FILES + ${CMAKE_CURRENT_LIST_DIR}/rsc/rublon.config.defaults + DESTINATION + share/rublon + COMPONENT + PAM +) + if (${ENABLE_TESTS}) enable_testing() endif() diff --git a/PAM/ssh/CMakeLists.txt b/PAM/ssh/CMakeLists.txt index 276aad4..88fa13c 100755 --- a/PAM/ssh/CMakeLists.txt +++ b/PAM/ssh/CMakeLists.txt @@ -11,12 +11,14 @@ set(INC ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/OTP.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/SMS.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/passcode_based_auth.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/method/websocket_based_auth.hpp ${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/rublon.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/sign.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/utils.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/rublon/websockets.hpp ) add_library(rublon-ssh diff --git a/PAM/ssh/include/rublon/core_handler_interface.hpp b/PAM/ssh/include/rublon/core_handler_interface.hpp index b03797d..a5865d7 100644 --- a/PAM/ssh/include/rublon/core_handler_interface.hpp +++ b/PAM/ssh/include/rublon/core_handler_interface.hpp @@ -15,5 +15,7 @@ class CoreHandlerInterface { rublon::log(LogLevel::Debug, "%s", "CoreHandlerInterface::request"); return static_cast< const Impl * >(this)->request(mr, path, body); } + + // tl::expected< Document, Error > }; } // namespace rublon diff --git a/PAM/ssh/include/rublon/curl.hpp b/PAM/ssh/include/rublon/curl.hpp index 6d345d5..e4ab3c6 100644 --- a/PAM/ssh/include/rublon/curl.hpp +++ b/PAM/ssh/include/rublon/curl.hpp @@ -18,8 +18,18 @@ #include +#include + namespace rublon { +inline bool replace(std::string& str, const std::string& from, const std::string& to) { + size_t start_pos = str.find(from); + if(start_pos == std::string::npos) + return false; + str.replace(start_pos, from.length(), to); + return true; +} + namespace { size_t WriteMemoryCallback(void * contents, size_t size, size_t nmemb, void * userp) { const size_t realsize = size * nmemb; diff --git a/PAM/ssh/include/rublon/init.hpp b/PAM/ssh/include/rublon/init.hpp index 9480851..727d853 100644 --- a/PAM/ssh/include/rublon/init.hpp +++ b/PAM/ssh/include/rublon/init.hpp @@ -8,11 +8,47 @@ #include #include +#include + namespace rublon { class Verify {}; } // namespace rublon namespace rublon { +std::pmr::string osName(std::pmr::memory_resource *mr) { + memory::MonotonicStackResource< 8 * 1024 > stackResource; + using Params = Configuration::Parameters; + Params configValues; + + 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"){ + return {_value, mr}; + } + } + + return {"unknown", mr}; +} + template < class MethodSelect_t = MethodSelect > class Init : public AuthenticationStep< Init< MethodSelect_t > > { using base_t = AuthenticationStep< Init< MethodSelect_t > >; @@ -33,24 +69,23 @@ class Init : public AuthenticationStep< Init< MethodSelect_t > > { template < typename PamInfo_t > void addParams(Document & coreRequest, const PamInfo_t & pam) const { - memory::MonotonicStackResource< 256 > stackResource; - std::pmr::string osname{&stackResource}; + memory::MonotonicStackResource< 512 > stackResource; + std::pmr::string releaseInfo{&stackResource}; auto & alloc = coreRequest.GetAllocator(); - const auto hasIssueFile = readFile("/etc/issue", osname); - const auto osnameTrimed = details::rtrim(hasIssueFile ? osname : "unknown"); - - if(not hasIssueFile) { + const auto os= osName(&stackResource); + + if(os== "unknown") { log(LogLevel::Warning, "No OS information available"); } - Value os{osnameTrimed.data(), static_cast< unsigned >(osnameTrimed.size()), alloc}; - Value params{rapidjson::kObjectType}; + 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", "v.0.0.1", alloc); /// TODO add version to cmake - params.AddMember("os", os, alloc); + params.AddMember("os", osNamePretty, alloc); coreRequest.AddMember("params", std::move(params), alloc); } diff --git a/PAM/ssh/include/rublon/method/PUSH.hpp b/PAM/ssh/include/rublon/method/PUSH.hpp new file mode 100644 index 0000000..b6046f8 --- /dev/null +++ b/PAM/ssh/include/rublon/method/PUSH.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +namespace rublon::method { + +class PUSH : public WebsocketBasedAuth { + public: + PUSH(std::string systemToken, std::string tid) : WebsocketBasedAuth(std::move(systemToken), std::move(tid), "PUSH") {} +}; + +} // namespace rublon::method diff --git a/PAM/ssh/include/rublon/method/method_select.hpp b/PAM/ssh/include/rublon/method/method_select.hpp index 8995723..9b8c34c 100644 --- a/PAM/ssh/include/rublon/method/method_select.hpp +++ b/PAM/ssh/include/rublon/method/method_select.hpp @@ -11,6 +11,7 @@ #include #include +#include template < class F > struct return_type; @@ -55,7 +56,7 @@ class MethodProxy { } private: - std::variant< method::OTP, method::SMS > _impl; + std::variant< method::OTP, method::SMS, method::PUSH > _impl; }; class PostMethod : public rublon::AuthenticationStep< PostMethod > { @@ -72,6 +73,8 @@ class PostMethod : public rublon::AuthenticationStep< PostMethod > { return MethodProxy{method::OTP{this->_systemToken, std::move(tid)}}; } else if(_method == "sms") { return MethodProxy{method::SMS{this->_systemToken, std::move(tid)}}; + } else if(_method == "push"){ + return MethodProxy{method::PUSH{this->_systemToken, std::move(tid)}}; } else @@ -147,16 +150,24 @@ class MethodSelect { if(method == "totp") { logMethodAvailable(method); pam.print("%d: Mobile TOTP", i + 1); - methods_id[++i] = "totp"; + methods_id[++i] = method; continue; } if(method == "sms") { logMethodAvailable(method); pam.print("%d: SMS code", i + 1); - methods_id[++i] = "sms"; + methods_id[++i] = method; continue; } + + if(method == "push" ){ + logMethodAvailable(method); + pam.print("%d: Mobile PUSH", i + 1); + methods_id[++i] = method; + continue; + } + logMethodNotAvailable(method); } }; diff --git a/PAM/ssh/include/rublon/method/websocket_based_auth.hpp b/PAM/ssh/include/rublon/method/websocket_based_auth.hpp new file mode 100644 index 0000000..2a39395 --- /dev/null +++ b/PAM/ssh/include/rublon/method/websocket_based_auth.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +namespace rublon::method { + +class WebsocketBasedAuth : public AuthenticationStep< WebsocketBasedAuth > { + using base_t = AuthenticationStep< WebsocketBasedAuth >; + + public: + const char * name = ""; + + WebsocketBasedAuth(std::string systemToken, std::string tid, const char * name) + : base_t(std::move(systemToken), std::move(tid)), name{name} {} + + template < typename Hander_t, typename PamInfo_t = LinuxPam > + tl::expected< AuthenticationStatus, Error > handle(const CoreHandlerInterface< Hander_t > & /*coreHandler*/, + const PamInfo_t & pam) const { + log(LogLevel::Info, "starting WS"); + + WebSocket ws; + pam.print("Waiting for user approval"); + auto aproved = ws.connect(this->_tid); + + return aproved ? AuthenticationStatus{AuthenticationStatus::Action::Confirmed} : + AuthenticationStatus{AuthenticationStatus::Action::Denied}; + } +}; + +} // namespace rublon::method diff --git a/PAM/ssh/include/rublon/pam.hpp b/PAM/ssh/include/rublon/pam.hpp index ba57873..4d6f7fb 100644 --- a/PAM/ssh/include/rublon/pam.hpp +++ b/PAM/ssh/include/rublon/pam.hpp @@ -45,14 +45,12 @@ class LinuxPam { void print(const char * fmt, Ti... ti) const noexcept { log(LogLevel::Debug, fmt, std::forward< Ti >(ti)...); pam_prompt(pamh, PAM_TEXT_INFO, nullptr, fmt, std::forward< Ti >(ti)...); - sleep(1); } template < typename Fun, typename... Ti > auto scan(Fun && f, const char * fmt, Ti... ti) const noexcept { char * response = nullptr; pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &response, fmt, std::forward< Ti >(ti)...); - sleep(1); if(response) { auto ret = f(response); free(response); diff --git a/PAM/ssh/include/rublon/rublon.hpp b/PAM/ssh/include/rublon/rublon.hpp index 112f908..076ce84 100644 --- a/PAM/ssh/include/rublon/rublon.hpp +++ b/PAM/ssh/include/rublon/rublon.hpp @@ -30,12 +30,12 @@ class RublonBase { - int authenticate() { - std::byte sharedMemory[32 * 1024] = {}; - std::pmr::monotonic_buffer_resource mr{sharedMemory, std::size(sharedMemory)}; - std::pmr::unsynchronized_pool_resource rublonPoolResource{&mr}; - std::pmr::set_default_resource(&rublonPoolResource); - } + // int authenticate() { + // std::byte sharedMemory[32 * 1024] = {}; + // std::pmr::monotonic_buffer_resource mr{sharedMemory, std::size(sharedMemory)}; + // std::pmr::unsynchronized_pool_resource rublonPoolResource{&mr}; + // std::pmr::set_default_resource(&rublonPoolResource); + // } }; } // namespace rublon diff --git a/PAM/ssh/include/rublon/utils.hpp b/PAM/ssh/include/rublon/utils.hpp index 231e790..9bc7abf 100644 --- a/PAM/ssh/include/rublon/utils.hpp +++ b/PAM/ssh/include/rublon/utils.hpp @@ -128,15 +128,15 @@ constexpr LogLevel g_level = LogLevel::Debug; constexpr bool syncLogFile = true; namespace details { - constexpr const char * logPath() { - constexpr auto path = "/var/log/rublon-ssh.log"; + constexpr auto path = "/tmp/rublon-ssh.log"; return path; } static void doLog(LogLevel level, const char * line) noexcept { auto fp = std::unique_ptr< FILE, int (*)(FILE *) >(fopen(logPath(), "a"), fclose); if(fp) { + ///TODO add transaction ID fprintf(fp.get(), "%s [%s] %s\n", dateStr().data(), LogLevelNames[( int ) level], line); if(syncLogFile) sync(); diff --git a/PAM/ssh/include/rublon/websockets.hpp b/PAM/ssh/include/rublon/websockets.hpp new file mode 100644 index 0000000..e6d215e --- /dev/null +++ b/PAM/ssh/include/rublon/websockets.hpp @@ -0,0 +1,245 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +namespace rublon { +#define SERVER_ADDRESS "wss://staging-core.rublon.net/ws/socket.io" +#define TIMEOUT_SECONDS 90 + +/// {"status":"ERROR","code":400,"result":{"exception":"MobileAppMissingException","code":32,"errorMessage":"No mobile app +/// detected","details":null}} + +class WebSocketConnectionListener { + public: + enum class Status { Disconnected, Conected }; + + private: + sio::client & _ws; + + std::mutex & _lock; + std::condition_variable_any & _cond; + + bool handshake{false}; + Status _status{Status::Disconnected}; + + public: + WebSocketConnectionListener(sio::client & ws, std::mutex & lock, std::condition_variable_any & cond) + : _ws(ws), _lock{lock}, _cond{cond} { + _ws.set_open_listener([this]() { this->on_connected(); }); + _ws.set_close_listener([this](sio::client::close_reason const & reason) { this->on_close(reason); }); + _ws.set_fail_listener([this]() { this->on_fail(); }); + } + + tl::expected< Status, bool > waitForConnection() { + std::lock_guard lock{_lock}; + if(!handshake) { + log(LogLevel::Info, "Waiting for connection"); + _cond.wait(_lock); + log(LogLevel::Info, "Connection OK!"); + } + + return _status; + } + + void on_connected() { + std::unique_lock{_lock}; + log(LogLevel::Info, "WebSocket connection estamblished"); + handshake = true; + _status = Status::Conected; + _cond.notify_all(); + } + + void on_close(sio::client::close_reason const & reason) { + std::unique_lock{_lock}; + log(LogLevel::Info, + "WebSocket connection %s", + reason == sio::client::close_reason::close_reason_drop ? "dropped" : "closed normally"); + handshake = true; + _status = Status::Disconnected; + _cond.notify_all(); + } + + void on_fail() { + std::unique_lock{_lock}; + log(LogLevel::Info, "WebSocket connection failed"); + handshake = true; + _status = Status::Disconnected; + _cond.notify_all(); + } +}; + +class WebSocketSingleShotEventListener { + public: + enum class Status { Approved, Denied, Expired }; + + private: + sio::client & _ws; + + std::mutex & _lock; + std::condition_variable_any & _cond; + + bool event_received = false; + Status _status{}; + + void printobj (std::size_t i, const std::map< std::string, sio::message::ptr > & objects) { + for(const auto &[name, msg]:objects){ + log(LogLevel::Debug, "Object %ld: %s", i, name.c_str()); + printMessage(i, msg); + } + }; + void printMessage (std::size_t i, const sio::message::ptr & message) { + switch(message->get_flag()) { + case sio::message::flag_integer: + log(LogLevel::Debug, "message %ld integer : %d", i, message->get_int()); + break; + case sio::message::flag_double: + log(LogLevel::Debug, "message %ld double : %f", i, message->get_double()); + break; + case sio::message::flag_string: + log(LogLevel::Debug, "message %ld string : %s", i, message->get_string().c_str()); + break; + case sio::message::flag_binary: + log(LogLevel::Debug, "message %ld binary : %s", i, message->get_binary()->c_str()); + break; + case sio::message::flag_array: + log(LogLevel::Debug, "message %ld array : blah", i); + break; + case sio::message::flag_object: + log(LogLevel::Debug, "message %ld object : obj ->", i); + printobj(i, message->get_map()); + break; + case sio::message::flag_boolean: + log(LogLevel::Debug, "message %ld bool : %s", i, message->get_bool() ? "yes" : "no"); + break; + case sio::message::flag_null: + log(LogLevel::Debug, "message %ld NULL", i); + break; + default: + log(LogLevel::Info, "Message with unknown type"); + } + }; + + void generic(sio::event & e, Status status) { + std::lock_guard lock{_lock}; + event_received = true; + _status = status; + + log(LogLevel::Debug, "event name : %s", e.get_name().c_str()); + log(LogLevel::Debug, "event nsp : %s", e.get_nsp().c_str()); + log(LogLevel::Debug, "event messages: %d", e.get_messages().size()); + + + for(std::size_t i = 0; i < e.get_messages().size(); i++) { + const auto & message = e.get_messages().at(i); + printMessage(i, message); + } + + _cond.notify_all(); + } + + void onConfirmed(sio::event & e) { + generic(e, Status::Approved); + log(LogLevel::Info, "Autentication confirmed over PUSH"); + } + + void onDenied(sio::event & e) { + generic(e, Status::Denied); + log(LogLevel::Info, "Autentication denied by user"); + sleep(30); + } + + void onExpired(sio::event & e) { + generic(e, Status::Expired); + log(LogLevel::Error, "Autentication call expierd"); + } + + void onErrorEvent(sio::message::ptr const & message) { + std::lock_guard lock{_lock}; + event_received = true; + log(LogLevel::Error, "Error: %s", message->get_string().c_str()); + _cond.notify_all(); + } + + void onAny(sio::event & e) { + std::lock_guard lock{_lock}; + event_received = true; + log(LogLevel::Info, "name %s", e.get_name().c_str()); + _cond.notify_all(); + } + + public: + WebSocketSingleShotEventListener(sio::client & ws, std::mutex & lock, std::condition_variable_any & cond) + : _ws{ws}, _lock{lock}, _cond{cond} { + _ws.socket()->on("transactionConfirmed", [this](sio::event & e) { this->onConfirmed(e); }); + _ws.socket()->on("transactionDenied", [this](sio::event & e) { this->onDenied(e); }); + _ws.socket()->on("transactionExpired", [this](sio::event & e) { this->onExpired(e); }); + + _ws.socket()->on_error([this](sio::message::ptr const & message) { this->onErrorEvent(message); }); + _ws.socket()->on_any([this](sio::event & e) { this->onAny(e); }); + } + + tl::expected< Status, bool > waitForEvent() { + std::lock_guard lock{_lock}; + if(!event_received) { + log(LogLevel::Info, "Waiting for confirmation"); + if(_cond.wait_until(_lock, std::chrono::system_clock::now() + std::chrono::minutes{2}) == std::cv_status::timeout) { + log(LogLevel::Info, "Waiting for confirmation failed due to WS timeout"); + } + } + + return _status; + } +}; + +class WebSocket { + sio::client ws; + + public: + std::string token; + + WebSocket() {} + + ~WebSocket() {} + + bool connect(std::string _token) { + std::mutex _lock; + std::condition_variable_any _cond; + + token = _token; + // ws.set_logs_verbose(); + + log(LogLevel::Debug, "start listening"); + ws.connect("wss://staging-core.rublon.net/ws/socket.io/"); + + const auto attachToTransactionConfirmationChannel = [&](const auto & status) -> tl::expected< bool, bool > { + /// TODO check status + auto message = std::dynamic_pointer_cast< sio::object_message >(sio::object_message::create()); + message->insert("channel", "transactionConfirmation." + _token); + ws.socket()->emit("subscribe", {message}); + + return true; /// TODO + }; + + const auto waitForUserAction = [&](const auto & connected) -> tl::expected< WebSocketSingleShotEventListener::Status, bool > { + WebSocketSingleShotEventListener eventListener{ws, _lock, _cond}; + return eventListener.waitForEvent(); + }; + + WebSocketConnectionListener l(ws, _lock, _cond); + auto v = l.waitForConnection() // + .and_then(attachToTransactionConfirmationChannel) + .and_then(waitForUserAction) + .value_or(WebSocketSingleShotEventListener::Status::Denied); + + return v == WebSocketSingleShotEventListener::Status::Approved; + } + + void listen() {} +}; +} // namespace rublon diff --git a/PAM/ssh/lib/CMakeLists.txt b/PAM/ssh/lib/CMakeLists.txt index 7cb5cc3..d0a421b 100644 --- a/PAM/ssh/lib/CMakeLists.txt +++ b/PAM/ssh/lib/CMakeLists.txt @@ -31,13 +31,27 @@ target_link_libraries(rublon-ssh-pam -lcurl -lssl -lcrypto + -lsioclient_tls ) +execute_process ( + COMMAND bash -c "awk -F= '/^ID=/{print $2}' /etc/os-release |tr -d '\n' | tr -d '\"'" + OUTPUT_VARIABLE outOS +) + +if(${outOS} MATCHES "centos|rhel" ) + message(INFO "detected rhen base system") + set(_destination /usr/lib64/security/) +elseif(${outOS} MATCHES "debian|ubuntu") + message(INFO "detected debian based system") + set(_destination /usr/lib/x86_64-linux-gnu/security/) +endif() + install( TARGETS rublon-ssh-pam DESTINATION - /usr/lib/x86_64-linux-gnu/security/ + ${_destination} COMPONENT PAM ) diff --git a/PAM/ssh/lib/pam.cpp b/PAM/ssh/lib/pam.cpp index 3580006..848f404 100644 --- a/PAM/ssh/lib/pam.cpp +++ b/PAM/ssh/lib/pam.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -12,8 +13,6 @@ #define DLL_PUBLIC __attribute__((visibility("default"))) -using namespace std; - DLL_PUBLIC int pam_sm_setcred([[maybe_unused]] pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unused]] int argc, @@ -32,6 +31,10 @@ DLL_PUBLIC int pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) { using namespace rublon; + rublon::log(LogLevel::Debug, "flags: {%d}, argc: {%d}", flags, argc); + // std::freopen(rublon::details::logPath(), "a+", stdout); + // std::freopen(rublon::details::logPath(), "a+", stderr); + LinuxPam pam{pamh}; auto rublonConfig = ConfigurationFactory{}.systemConfig(); @@ -72,22 +75,20 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu pam.print("\n RUBLON authentication bypased"); return PAM_SUCCESS; } - - if(error.is< HttpError >()){ + + if(error.is< HttpError >()) { pam.print("\n RUBLON server unavalible"); - if(rublonConfig->parameters.bypass ){ + if(rublonConfig->parameters.bypass) { pam.print("\n RUBLON authentication bypased"); return PAM_SUCCESS; - } - else{ + } else { pam.print("RUBLON authentication FAILED"); return PAM_MAXTRIES; } } - - if(error.is()){ - pam.print("\n RUBLON server returned '%s' exception", - error.get().reson.c_str()); + + if(error.is< CoreHandlerError >()) { + pam.print("\n RUBLON server returned '%s' exception", error.get< CoreHandlerError >().reson.c_str()); } pam.print("RUBLON authentication FAILED"); rublon::log(LogLevel::Warning, "User login failed"); diff --git a/PAM/ssh/tests/core_response_generator.hpp b/PAM/ssh/tests/core_response_generator.hpp index 3049411..3a9fd10 100644 --- a/PAM/ssh/tests/core_response_generator.hpp +++ b/PAM/ssh/tests/core_response_generator.hpp @@ -44,4 +44,7 @@ static constexpr const char * result_broken_template = // static constexpr const char * wrongPasscode = // R"json({"status":"OK","result":{"error":"Hmm, that's not the right code. Try again."}})json"; +static constexpr const char * projectError = + R"json({"status":"ERROR","code":400,"result":{"exception":"APIException","code":10,"errorMessage":"Project error","details":null}})json"; + } // namespace diff --git a/os/centos/stream9/Vagrantfile b/os/centos/stream9/Vagrantfile index bec545f..e8f40f8 100755 --- a/os/centos/stream9/Vagrantfile +++ b/os/centos/stream9/Vagrantfile @@ -15,7 +15,7 @@ Vagrant.configure("2") do |config| # Bridged networks make the machine appear as another physical device on # your network. config.vm.network "public_network" - + # Share an additional folder to the guest VM. The first argument is # the path on the host to the actual folder. The second argument is # the path on the guest to mount the folder. And the optional third @@ -36,7 +36,18 @@ Vagrant.configure("2") do |config| config.vm.provision "shell", inline: <<-SHELL yum update - yum install -y gcc openssl-devel curl-devel pam-devel git rapidjson-devel cmake + yum install -y gcc openssl-devel curl-devel pam-devel git rapidjson-devel cmake policycoreutils-devel checkpolicy + + # get dependencies + 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 + + #handle semodule + cd /home/vagrant/Rublon-Linux/service + checkmodule -M -m -o login_rublon.mod login_rublon.te + semodule_package -o login_rublon.pp -m login_rublon.mod # Build project cd /home/vagrant/Rublon-Linux @@ -46,11 +57,18 @@ Vagrant.configure("2") do |config| sudo cmake --install build sudo install -m 644 rsc/rublon.config.defaults /etc/rublon.config + #handle semodule + cd /home/vagrant/Rublon-Linux/service + semodule -i login_rublon.pp + # Register Rublon pam - sed -i 's/UsePAM .*/UsePAM yes/' /etc/ssh/sshd_config - sed -i 's/ChallengeResponseAuthentication .*/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config - grep -q -e 'auth required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aauth required pam_rublon.so' /etc/pam.d/sshd - grep -q -e 'account required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aaccount required pam_rublon.so' /etc/pam.d/sshd + #grep -q -e '#auth substack password-auth' /etc/pam.d/sshd || sed -i -e 's/auth substack password-auth/#auth substack password-auth/g' /etc/pam.d/sshd + #grep -q -e 'auth requisite pam_unix.so' /etc/pam.d/sshd || sed -i '\$aauth requisite pam_unix.so' /etc/pam.d/sshd + #grep -q -e 'auth required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aauth required pam_rublon.so' /etc/pam.d/sshd + #grep -q -e 'account required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aaccount required pam_rublon.so' /etc/pam.d/sshd + + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd systemctl restart sshd.service SHELL diff --git a/os/debian/11/Vagrantfile b/os/debian/11/Vagrantfile index cf61e67..163a527 100755 --- a/os/debian/11/Vagrantfile +++ b/os/debian/11/Vagrantfile @@ -23,6 +23,8 @@ Vagrant.configure("2") do |config| config.vm.synced_folder "../../..", "/home/vagrant/Rublon-Linux" config.vm.provider "virtualbox" do |vb| + vb.memory = 1024 + vb.cpus = 4 # Display the VirtualBox GUI when booting the machine vb.gui = true @@ -47,6 +49,12 @@ Vagrant.configure("2") do |config| rapidjson-dev \ cmake + # get dependencies + 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 + # Build project cd /home/vagrant/Rublon-Linux cmake -B build && cmake --build build @@ -56,11 +64,21 @@ Vagrant.configure("2") do |config| sudo install -m 644 rsc/rublon.config.defaults /etc/rublon.config # Register Rublon pam - sed -i 's/UsePAM .*/UsePAM yes/' /etc/ssh/sshd_config - sed -i 's/ChallengeResponseAuthentication .*/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config - grep -q -e 'auth required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aauth required pam_rublon.so' /etc/pam.d/sshd - grep -q -e 'account required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aaccount required pam_rublon.so' /etc/pam.d/sshd + SSHD_CONF=/etc/ssh/sshd_config + SSHD_PAM_CONF=/etc/pam.d/sshd + + grep -qe "^PasswordAuthentication" $SSHD_CONF && sed -i 's/^#*PasswordAuthentication[[:space:]]\+.*/PasswordAuthentication yes/' $SSHD_CONF || echo "PasswordAuthentication yes" >> $SSHD_CONF + grep -qe "^ChallengeResponseAuthentication" $SSHD_CONF && sed -i 's/^#*ChallengeResponseAuthentication[[:space:]]\+.*/ChallengeResponseAuthentication yes/' $SSHD_CONF || echo "ChallengeResponseAuthentication yes" >> $SSHD_CONF + grep -qe "^UsePAM" $SSHD_CONF && sed -i 's/^#*UsePAM[[:space:]]\+.*/UsePAM yes/' $SSHD_CONF || echo "UsePAM yes" >> $SSHD_CONF + + 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 + + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd service sshd restart SHELL + + config.vm.provision "file", source: "/etc/rublon.config", destination: "/etc/rublon.config" end diff --git a/os/debian/12/Vagrantfile b/os/debian/12/Vagrantfile index d6d9a3f..b611156 100755 --- a/os/debian/12/Vagrantfile +++ b/os/debian/12/Vagrantfile @@ -56,10 +56,18 @@ Vagrant.configure("2") do |config| sudo install -m 644 rsc/rublon.config.defaults /etc/rublon.config # Register Rublon pam - sed -i 's/UsePAM .*/UsePAM yes/' /etc/ssh/sshd_config - sed -i 's/ChallengeResponseAuthentication .*/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config - grep -q -e 'auth required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aauth required pam_rublon.so' /etc/pam.d/sshd - grep -q -e 'account required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aaccount required pam_rublon.so' /etc/pam.d/sshd + SSHD_CONF=/etc/ssh/sshd_config + SSHD_PAM_CONF=/etc/pam.d/sshd + + grep -qe "^PasswordAuthentication" $SSHD_CONF && sed -i 's/^#*PasswordAuthentication[[:space:]]\+.*/PasswordAuthentication yes/' $SSHD_CONF || echo "PasswordAuthentication yes" >> $SSHD_CONF + grep -qe "^ChallengeResponseAuthentication" $SSHD_CONF && sed -i 's/^#*ChallengeResponseAuthentication[[:space:]]\+.*/ChallengeResponseAuthentication yes/' $SSHD_CONF || echo "ChallengeResponseAuthentication yes" >> $SSHD_CONF + grep -qe "^UsePAM" $SSHD_CONF && sed -i 's/^#*UsePAM[[:space:]]\+.*/UsePAM yes/' $SSHD_CONF || echo "UsePAM yes" >> $SSHD_CONF + + 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 + + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd service sshd restart end diff --git a/os/rhel/9/Vagrantfile b/os/rhel/9/Vagrantfile new file mode 100755 index 0000000..3aed343 --- /dev/null +++ b/os/rhel/9/Vagrantfile @@ -0,0 +1,79 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Default user +# ---------------------- +# login: vagrant +# pass: vagrant +Vagrant.configure("2") do |config| + # Basic configuration + config.vm.provider "virtualbox" + config.vm.box = "generic/rhel9" + config.ssh.forward_agent = true + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + config.vm.synced_folder "../../..", "/home/vagrant/Rublon-Linux" + + config.vm.provider "virtualbox" do |vb| + vb.memory = 1024 + vb.cpus = 4 + # Display the VirtualBox GUI when booting the machine + vb.gui = true + + # 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 + + yum update + yum install -y gcc openssl-devel curl-devel pam-devel git rapidjson-devel cmake + + # get dependencies + 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 + + #handle semodule + cd /home/vagrant/Rublon-Linux/service + checkmodule -M -m -o login_rublon.mod login_rublon.te + semodule_package -o login_rublon.pp -m login_rublon.mod + + # Build project + cd /home/vagrant/Rublon-Linux + cmake -B build && cmake --build build + # Install + sudo cmake --install build + sudo install -m 644 rsc/rublon.config.defaults /etc/rublon.config + + #handle semodule + cd /home/vagrant/Rublon-Linux/service + semodule -i login_rublon.pp + + # Register Rublon pam + sed -i 's/UsePAM .*/UsePAM yes/' /etc/ssh/sshd_config + sed -i 's/ChallengeResponseAuthentication .*/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config + grep -q -e 'auth required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aauth required pam_rublon.so' /etc/pam.d/sshd + grep -q -e 'account required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aaccount required pam_rublon.so' /etc/pam.d/sshd + + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd + + systemctl restart sshd.service + SHELL + + config.vm.provision "file", source: "/etc/rublon.config", destination: "/etc/rublon.config" +end + diff --git a/os/ubuntu/20.04/Vagrantfile b/os/ubuntu/20.04/Vagrantfile index 4e7468a..727bc63 100755 --- a/os/ubuntu/20.04/Vagrantfile +++ b/os/ubuntu/20.04/Vagrantfile @@ -46,6 +46,12 @@ Vagrant.configure("2") do |config| git \ rapidjson-dev \ cmake + + # get dependencies + 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 # Build project cd /home/vagrant/Rublon-Linux @@ -56,10 +62,18 @@ Vagrant.configure("2") do |config| sudo install -m 644 rsc/rublon.config.defaults /etc/rublon.config # Register Rublon pam - sed -i 's/UsePAM .*/UsePAM yes/' /etc/ssh/sshd_config - sed -i 's/ChallengeResponseAuthentication .*/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config - grep -q -e 'auth required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aauth required pam_rublon.so' /etc/pam.d/sshd - grep -q -e 'account required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aaccount required pam_rublon.so' /etc/pam.d/sshd + SSHD_CONF=/etc/ssh/sshd_config + SSHD_PAM_CONF=/etc/pam.d/sshd + + grep -qe "^PasswordAuthentication" $SSHD_CONF && sed -i 's/^#*PasswordAuthentication[[:space:]]\+.*/PasswordAuthentication yes/' $SSHD_CONF || echo "PasswordAuthentication yes" >> $SSHD_CONF + grep -qe "^ChallengeResponseAuthentication" $SSHD_CONF && sed -i 's/^#*ChallengeResponseAuthentication[[:space:]]\+.*/ChallengeResponseAuthentication yes/' $SSHD_CONF || echo "ChallengeResponseAuthentication yes" >> $SSHD_CONF + grep -qe "^UsePAM" $SSHD_CONF && sed -i 's/^#*UsePAM[[:space:]]\+.*/UsePAM yes/' $SSHD_CONF || echo "UsePAM yes" >> $SSHD_CONF + + 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 + + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd service sshd restart SHELL diff --git a/os/ubuntu/22.04/Vagrantfile b/os/ubuntu/22.04/Vagrantfile index 9df6bfb..5ec8a93 100755 --- a/os/ubuntu/22.04/Vagrantfile +++ b/os/ubuntu/22.04/Vagrantfile @@ -47,6 +47,12 @@ Vagrant.configure("2") do |config| rapidjson-dev \ cmake + # get dependencies + 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 + # Build project cd /home/vagrant/Rublon-Linux cmake -B build && cmake --build build @@ -56,10 +62,18 @@ Vagrant.configure("2") do |config| sudo install -m 644 rsc/rublon.config.defaults /etc/rublon.config # Register Rublon pam - sed -i 's/UsePAM .*/UsePAM yes/' /etc/ssh/sshd_config - sed -i 's/ChallengeResponseAuthentication .*/ChallengeResponseAuthentication yes/' /etc/ssh/sshd_config - grep -q -e 'auth required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aauth required pam_rublon.so' /etc/pam.d/sshd - grep -q -e 'account required pam_rublon.so' /etc/pam.d/sshd || sed -i '\$aaccount required pam_rublon.so' /etc/pam.d/sshd + SSHD_CONF=/etc/ssh/sshd_config + SSHD_PAM_CONF=/etc/pam.d/sshd + + grep -qe "^PasswordAuthentication" $SSHD_CONF && sed -i 's/^#*PasswordAuthentication[[:space:]]\+.*/PasswordAuthentication yes/' $SSHD_CONF || echo "PasswordAuthentication yes" >> $SSHD_CONF + grep -qe "^ChallengeResponseAuthentication" $SSHD_CONF && sed -i 's/^#*ChallengeResponseAuthentication[[:space:]]\+.*/ChallengeResponseAuthentication yes/' $SSHD_CONF || echo "ChallengeResponseAuthentication yes" >> $SSHD_CONF + grep -qe "^UsePAM" $SSHD_CONF && sed -i 's/^#*UsePAM[[:space:]]\+.*/UsePAM yes/' $SSHD_CONF || echo "UsePAM yes" >> $SSHD_CONF + + 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 + + useradd -s /bin/bash -m bwi + echo "bwi:bwi"|chpasswd service sshd restart SHELL diff --git a/pack.cmake b/pack.cmake index 140e2d8..06388aa 100644 --- a/pack.cmake +++ b/pack.cmake @@ -29,6 +29,9 @@ set(CPACK_DEB_COMPONENT_INSTALL YES) set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS YES) set(CPACK_GENERATOR "DEB") -set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libcurl(>= 7.0.0), libc(>= 2.0)") +# set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libcurl4(>= 7.0.0), libc(>= 2.0)") +# set(CPACK_DEBIAN_PACKAGE_DEPENDS "libcurl4(>= 7.0.0), libc(>= 2.0)") + +set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/service/postinst") include(CPack) diff --git a/service/login_rublon.mod b/service/login_rublon.mod new file mode 100644 index 0000000..332e617 Binary files /dev/null and b/service/login_rublon.mod differ diff --git a/service/login_rublon.pp b/service/login_rublon.pp new file mode 100644 index 0000000..ea0b2f8 Binary files /dev/null and b/service/login_rublon.pp differ diff --git a/service/login_rublon.te b/service/login_rublon.te new file mode 100644 index 0000000..2fe32e5 --- /dev/null +++ b/service/login_rublon.te @@ -0,0 +1,12 @@ +module login_rublon 1.0; +require { +type http_port_t; +type soundd_port_t; +type http_cache_port_t; +type sshd_t; +type unreserved_port_t; +class tcp_socket name_connect; +class udp_socket name_bind; +}; +allow sshd_t {http_port_t http_cache_port_t soundd_port_t}:tcp_socket name_connect; +allow sshd_t {unreserved_port_t}:udp_socket name_bind; diff --git a/service/postinst b/service/postinst new file mode 100644 index 0000000..a332014 --- /dev/null +++ b/service/postinst @@ -0,0 +1,27 @@ +#!/bin/bash + + +if [ ! -f /etc/rublon/rublon.config ] +then + mkdir -p /etc/rublon + cp -a /usr/share/rublon/rublon.config.defaults /etc/rublon/rublon.config +fi + +# get system id +. /etc/os-release + +SSHD_CONF=/etc/ssh/sshd_config +SSHD_PAM_CONF=/etc/pam.d/sshd + +grep -qe "^PasswordAuthentication" $SSHD_CONF && \ + sed -i 's/^#*PasswordAuthentication[[:space:]]\+.*/PasswordAuthentication yes/' $SSHD_CONF || \ + echo "PasswordAuthentication yes" >> $SSHD_CONF +grep -qe "^ChallengeResponseAuthentication" $SSHD_CONF && \ + sed -i 's/^#*ChallengeResponseAuthentication[[:space:]]\+.*/ChallengeResponseAuthentication yes/' $SSHD_CONF || \ + echo "ChallengeResponseAuthentication yes" >> $SSHD_CONF +grep -qe "^UsePAM" $SSHD_CONF && \ + sed -i 's/^#*UsePAM[[:space:]]\+.*/UsePAM yes/' $SSHD_CONF || \ + echo "UsePAM yes" >> $SSHD_CONF + +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