Bwi/v2.3.2 (#19)
* Prevent printing in noninteractive mode * Allow PAM modules to be configurated directly in pam.d * Configuration should be redable by everybody * Add a way to read ip address in when no IP is awailable * Enable read ip from pam * Fix veritas BUG
This commit is contained in:
parent
de26451445
commit
351964199a
@ -7,7 +7,7 @@ include(GNUInstallDirs)
|
|||||||
|
|
||||||
set(PROJECT_VERSION_MAJOR 2)
|
set(PROJECT_VERSION_MAJOR 2)
|
||||||
set(PROJECT_VERSION_MINOR 3)
|
set(PROJECT_VERSION_MINOR 3)
|
||||||
set(PROJECT_VERSION_PATCH 1)
|
set(PROJECT_VERSION_PATCH 2)
|
||||||
set(RUBLON_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
set(RUBLON_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|||||||
@ -1,10 +1,3 @@
|
|||||||
#include <security/pam_appl.h>
|
|
||||||
#include <security/pam_client.h>
|
|
||||||
#include <security/pam_ext.h>
|
|
||||||
#include <security/pam_misc.h>
|
|
||||||
#include <security/pam_modules.h>
|
|
||||||
#include <syslog.h>
|
|
||||||
|
|
||||||
#include <rublon/bits.hpp>
|
#include <rublon/bits.hpp>
|
||||||
#include <rublon/check_application.hpp>
|
#include <rublon/check_application.hpp>
|
||||||
#include <rublon/error.hpp>
|
#include <rublon/error.hpp>
|
||||||
@ -13,7 +6,7 @@
|
|||||||
#include <rublon/rublon.hpp>
|
#include <rublon/rublon.hpp>
|
||||||
#include <rublon/utils.hpp>
|
#include <rublon/utils.hpp>
|
||||||
|
|
||||||
int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) {
|
int main(int argc, const char ** argv) {
|
||||||
using namespace rublon;
|
using namespace rublon;
|
||||||
details::initLog();
|
details::initLog();
|
||||||
PamStub pam{};
|
PamStub pam{};
|
||||||
@ -38,7 +31,7 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Session session{pam};
|
Session session{pam};
|
||||||
auto ok = rublon::RublonFactory{}.initializeSession(session);
|
auto ok = rublon::RublonFactory{}.initializeSession(session, 0, argc, argv);
|
||||||
if(not ok.has_value()) {
|
if(not ok.has_value()) {
|
||||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "rublon/configuration.hpp"
|
#include <rublon/configuration.hpp>
|
||||||
#include "rublon/session.hpp"
|
#include <rublon/session.hpp>
|
||||||
#include <cstdio>
|
|
||||||
#include <rublon/bits.hpp>
|
#include <rublon/bits.hpp>
|
||||||
#include <rublon/core_handler.hpp>
|
#include <rublon/core_handler.hpp>
|
||||||
#include <rublon/curl.hpp>
|
#include <rublon/curl.hpp>
|
||||||
@ -11,11 +10,6 @@
|
|||||||
#include <rublon/memory.hpp>
|
#include <rublon/memory.hpp>
|
||||||
#include <rublon/utils.hpp>
|
#include <rublon/utils.hpp>
|
||||||
|
|
||||||
#include <rapidjson/prettywriter.h>
|
|
||||||
#include <rapidjson/rapidjson.h>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace rublon {
|
namespace rublon {
|
||||||
|
|
||||||
struct closefile_deleter {
|
struct closefile_deleter {
|
||||||
|
|||||||
@ -44,8 +44,9 @@ class Configuration {
|
|||||||
|
|
||||||
class ConfigurationReader {
|
class ConfigurationReader {
|
||||||
public:
|
public:
|
||||||
ConfigurationReader(std::pmr::memory_resource * memResource, std::string_view filepath) : memoryResource(memResource) {
|
ConfigurationReader(std::pmr::memory_resource * memResource, std::string_view filepath, int argc, const char **argv) : _memoryResource(memResource) {
|
||||||
loadFromFile(filepath);
|
loadFromFile(filepath);
|
||||||
|
loadFromArgv(argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config from file path
|
// Load config from file path
|
||||||
@ -60,22 +61,42 @@ class ConfigurationReader {
|
|||||||
line.reserve(300);
|
line.reserve(300);
|
||||||
|
|
||||||
while(std::getline(file, line)) {
|
while(std::getline(file, line)) {
|
||||||
details::trimInPlace(line);
|
parseLine(line);
|
||||||
if(!line.length())
|
}
|
||||||
continue;
|
}
|
||||||
|
|
||||||
if(line[0] == '#' || line[0] == ';')
|
void loadFromArgv(int argc, const char **argv) {
|
||||||
continue;
|
using namespace memory::literals;
|
||||||
|
|
||||||
|
for(int i = 0; i < argc; ++i) {
|
||||||
|
parseLine(argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseLine(std::string_view line) {
|
||||||
|
line = details::trim(line);
|
||||||
|
|
||||||
|
if (line.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (line.front() == '#' || line.front() == ';')
|
||||||
|
return;
|
||||||
|
|
||||||
auto posEqual = line.find('=');
|
auto posEqual = line.find('=');
|
||||||
std::pmr::string key{line.substr(0, posEqual), &memoryResource};
|
if (posEqual == std::string::npos)
|
||||||
std::pmr::string value{line.substr(posEqual + 1), &memoryResource};
|
return;
|
||||||
|
|
||||||
|
std::pmr::string key{line.substr(0, posEqual), _memoryResource};
|
||||||
|
std::pmr::string value{line.substr(posEqual + 1), _memoryResource};
|
||||||
|
|
||||||
details::trimInPlace(key);
|
details::trimInPlace(key);
|
||||||
details::trimInPlace(value);
|
details::trimInPlace(value);
|
||||||
|
|
||||||
keyValues[std::move(key)] = std::move(value);
|
if (auto it = _keyValues.find(key); it != _keyValues.end()) {
|
||||||
|
log(LogLevel::Debug, "Overwriting existing key: %s (old value: %s) with %s", key.c_str(), it->second.c_str(), value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_keyValues[std::move(key)] = std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load values into Configuration object, with defaults provided
|
// Load values into Configuration object, with defaults provided
|
||||||
@ -86,12 +107,12 @@ class ConfigurationReader {
|
|||||||
|
|
||||||
// Helper lambdas for conversion
|
// Helper lambdas for conversion
|
||||||
auto getStringOpt = [&](const string & key) -> std::optional< std::pmr::string > {
|
auto getStringOpt = [&](const string & key) -> std::optional< std::pmr::string > {
|
||||||
auto it = keyValues.find(key);
|
auto it = _keyValues.find(key);
|
||||||
if(it == keyValues.end()) {
|
if(it == _keyValues.end()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return string{it->second.data(), it->second.size(), memoryResource};
|
return string{it->second.data(), it->second.size(), _memoryResource};
|
||||||
};
|
};
|
||||||
|
|
||||||
auto getStringReq = [&](const string & key) -> tl::expected< std::pmr::string, ConfigurationError > {
|
auto getStringReq = [&](const string & key) -> tl::expected< std::pmr::string, ConfigurationError > {
|
||||||
@ -106,16 +127,16 @@ class ConfigurationReader {
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto getInt = [&](const string & key) -> std::optional< int > {
|
auto getInt = [&](const string & key) -> std::optional< int > {
|
||||||
auto it = keyValues.find(key);
|
auto it = _keyValues.find(key);
|
||||||
if(it == keyValues.end())
|
if(it == _keyValues.end())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return conv::to_uint32opt(it->second);
|
return conv::to_uint32opt(it->second);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto getBool = [&](const string & key) -> std::optional< bool > {
|
auto getBool = [&](const string & key) -> std::optional< bool > {
|
||||||
memory::MonotonicStackResource< 32 > memoryResource;
|
memory::MonotonicStackResource< 32 > memoryResource;
|
||||||
auto it = keyValues.find(key);
|
auto it = _keyValues.find(key);
|
||||||
if(it == keyValues.end())
|
if(it == _keyValues.end())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if(it->second.size() > 5) {
|
if(it->second.size() > 5) {
|
||||||
@ -135,8 +156,8 @@ class ConfigurationReader {
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto getFailMode = [&](const string & key) -> std::optional< FailMode > {
|
auto getFailMode = [&](const string & key) -> std::optional< FailMode > {
|
||||||
auto it = keyValues.find(key);
|
auto it = _keyValues.find(key);
|
||||||
if(it == keyValues.end())
|
if(it == _keyValues.end())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
auto val = it->second;
|
auto val = it->second;
|
||||||
std::transform(val.begin(), val.end(), val.begin(), [](auto c) { return std::tolower(c); });
|
std::transform(val.begin(), val.end(), val.begin(), [](auto c) { return std::tolower(c); });
|
||||||
@ -310,8 +331,8 @@ class ConfigurationReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::pmr::memory_resource * memoryResource;
|
std::pmr::memory_resource * _memoryResource;
|
||||||
std::pmr::map< std::pmr::string, std::pmr::string > keyValues{memoryResource};
|
std::pmr::map< std::pmr::string, std::pmr::string > _keyValues{_memoryResource};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rublon
|
} // namespace rublon
|
||||||
|
|||||||
@ -224,6 +224,41 @@ class RublonCheckApplicationException {
|
|||||||
ErrorClass errorClass;
|
ErrorClass errorClass;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class RublonRuntimeException {
|
||||||
|
public:
|
||||||
|
enum ErrorClass {
|
||||||
|
CantCreateSocket,
|
||||||
|
ConnectionFailed,
|
||||||
|
CantGetSocketName,
|
||||||
|
CantReadLocalIP
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr static auto errorClassPrettyName = make_array< std::string_view >( //
|
||||||
|
"CantCreateSocket",
|
||||||
|
"ConnectionFailed",
|
||||||
|
"CantGetSocketName",
|
||||||
|
"CantReadLocalIP");
|
||||||
|
constexpr static auto prettyName = "Rublon Runtime Exception";
|
||||||
|
|
||||||
|
RublonRuntimeException(ErrorClass e = CantReadLocalIP) : errorClass{e} {}
|
||||||
|
|
||||||
|
constexpr const char * what() const {
|
||||||
|
return errorClassPrettyName[static_cast< int >(errorClass)].data();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional< RublonRuntimeException > fromString(std::string_view name) {
|
||||||
|
for(std::size_t i{}; i < errorClassPrettyName.size(); i++) {
|
||||||
|
if(errorClassPrettyName.at(i) == name) {
|
||||||
|
return std::make_optional(RublonRuntimeException{static_cast< ErrorClass >(i)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorClass errorClass;
|
||||||
|
};
|
||||||
|
|
||||||
class Error {
|
class Error {
|
||||||
using Error_t = std::variant< ConfigurationError,
|
using Error_t = std::variant< ConfigurationError,
|
||||||
CoreHandlerError,
|
CoreHandlerError,
|
||||||
@ -231,7 +266,8 @@ class Error {
|
|||||||
WerificationError,
|
WerificationError,
|
||||||
MethodError,
|
MethodError,
|
||||||
RublonAuthenticationInterrupt,
|
RublonAuthenticationInterrupt,
|
||||||
RublonCheckApplicationException >;
|
RublonCheckApplicationException,
|
||||||
|
RublonRuntimeException >;
|
||||||
Error_t _error;
|
Error_t _error;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -242,7 +278,8 @@ class Error {
|
|||||||
k_WerificationError,
|
k_WerificationError,
|
||||||
k_MethodError,
|
k_MethodError,
|
||||||
k_RublonAuthenticationInterrupt,
|
k_RublonAuthenticationInterrupt,
|
||||||
k_RublonCheckApplication
|
k_RublonCheckApplication,
|
||||||
|
k_RublonRuntimeException
|
||||||
};
|
};
|
||||||
|
|
||||||
Error() = default;
|
Error() = default;
|
||||||
@ -254,6 +291,7 @@ class Error {
|
|||||||
Error(WerificationError error) : _error{error} {}
|
Error(WerificationError error) : _error{error} {}
|
||||||
Error(RublonAuthenticationInterrupt error) : _error{error} {}
|
Error(RublonAuthenticationInterrupt error) : _error{error} {}
|
||||||
Error(RublonCheckApplicationException error) : _error{error} {}
|
Error(RublonCheckApplicationException error) : _error{error} {}
|
||||||
|
Error(RublonRuntimeException error) : _error{error} {}
|
||||||
|
|
||||||
Error(const Error &) = default;
|
Error(const Error &) = default;
|
||||||
Error(Error &&) = default;
|
Error(Error &&) = default;
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "rublon/utils.hpp"
|
||||||
#include <rublon/authentication_step_interface.hpp>
|
#include <rublon/authentication_step_interface.hpp>
|
||||||
#include <rublon/bits.hpp>
|
#include <rublon/bits.hpp>
|
||||||
#include <rublon/configuration.hpp>
|
#include <rublon/configuration.hpp>
|
||||||
#include <rublon/json.hpp>
|
#include <rublon/json.hpp>
|
||||||
#include <rublon/method/method_select.hpp>
|
#include <rublon/method/method_select.hpp>
|
||||||
#include <rublon/session.hpp>
|
#include <rublon/session.hpp>
|
||||||
|
#include <rublon/ip_utils.hpp>
|
||||||
#include <sys/utsname.h>
|
|
||||||
|
|
||||||
namespace rublon {
|
namespace rublon {
|
||||||
|
|
||||||
@ -40,6 +40,8 @@ class Init : public AuthenticationStep {
|
|||||||
|
|
||||||
void addParams(Document & coreRequest, const Pam_t & pam) const {
|
void addParams(Document & coreRequest, const Pam_t & pam) const {
|
||||||
using namespace memory::literals;
|
using namespace memory::literals;
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
memory::MonotonicStackResource< 2_kB > memoryResource;
|
memory::MonotonicStackResource< 2_kB > memoryResource;
|
||||||
auto & alloc = coreRequest.GetAllocator();
|
auto & alloc = coreRequest.GetAllocator();
|
||||||
|
|
||||||
@ -50,16 +52,28 @@ class Init : public AuthenticationStep {
|
|||||||
log(LogLevel::Warning, "No OS information available");
|
log(LogLevel::Warning, "No OS information available");
|
||||||
}
|
}
|
||||||
|
|
||||||
Value ip{pam.ip().get(), alloc};
|
|
||||||
|
|
||||||
Value params{rapidjson::kObjectType};
|
Value params{rapidjson::kObjectType};
|
||||||
params.AddMember("appVer", RUBLON_VERSION_STRING, alloc);
|
params.AddMember("appVer", RUBLON_VERSION_STRING, alloc);
|
||||||
if(not host.empty())
|
if(not host.empty())
|
||||||
params.AddMember("hostName", Value{os.c_str(), static_cast< unsigned >(host.size()), alloc}, alloc);
|
params.AddMember("hostName", Value{os.c_str(), static_cast< unsigned >(host.size()), alloc}, alloc);
|
||||||
|
|
||||||
if(_session.inNonInteractiveMode())
|
if(_session.inNonInteractiveMode())
|
||||||
params.AddMember("mode", "noninteractive", alloc);
|
params.AddMember("mode", "noninteractive", alloc);
|
||||||
|
|
||||||
params.AddMember("os", Value{os.c_str(), static_cast< unsigned >(os.size()), alloc}, alloc);
|
params.AddMember("os", Value{os.c_str(), static_cast< unsigned >(os.size()), alloc}, alloc);
|
||||||
|
|
||||||
|
auto ip = pam.ip();
|
||||||
|
// ip has always a value, can be empty but the value should be set
|
||||||
|
if(ip.get() != ""sv){
|
||||||
params.AddMember("userIP", Value{pam.ip().get(), alloc}, alloc);
|
params.AddMember("userIP", Value{pam.ip().get(), alloc}, alloc);
|
||||||
|
} else {
|
||||||
|
getDefaultRouteIp(&memoryResource) //
|
||||||
|
.and_then([&](const auto & ip) {
|
||||||
|
params.AddMember("userIP", Value{ip.c_str(), alloc}, alloc);
|
||||||
|
return tl::expected< void, Error >{};
|
||||||
|
})
|
||||||
|
.or_else([](auto) { log(LogLevel::Warning, "There is no UserIP that can be used"); });
|
||||||
|
}
|
||||||
|
|
||||||
coreRequest.AddMember("params", std::move(params), alloc);
|
coreRequest.AddMember("params", std::move(params), alloc);
|
||||||
}
|
}
|
||||||
|
|||||||
96
PAM/ssh/include/rublon/ip_utils.hpp
Normal file
96
PAM/ssh/include/rublon/ip_utils.hpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "rublon/utils.hpp"
|
||||||
|
#include <memory_resource>
|
||||||
|
#include <rublon/error.hpp>
|
||||||
|
#include <rublon/memory.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <tl/expected.hpp>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace rublon {
|
||||||
|
|
||||||
|
using socket_t = int;
|
||||||
|
constexpr socket_t invalid_socket = -1;
|
||||||
|
|
||||||
|
class unique_fd {
|
||||||
|
socket_t fd_ = invalid_socket;
|
||||||
|
|
||||||
|
public:
|
||||||
|
unique_fd() = default;
|
||||||
|
explicit unique_fd(socket_t fd) : fd_(fd) {}
|
||||||
|
|
||||||
|
~unique_fd() {
|
||||||
|
close(fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_fd(const unique_fd &) = delete;
|
||||||
|
unique_fd & operator=(const unique_fd &) = delete;
|
||||||
|
|
||||||
|
unique_fd(unique_fd && other) noexcept : fd_(std::exchange(other.fd_, invalid_socket)) {}
|
||||||
|
unique_fd & operator=(unique_fd && other) noexcept {
|
||||||
|
if(this != &other) {
|
||||||
|
close(fd_);
|
||||||
|
fd_ = std::exchange(other.fd_, invalid_socket);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket_t get() const noexcept {
|
||||||
|
return fd_;
|
||||||
|
}
|
||||||
|
socket_t release() noexcept {
|
||||||
|
return std::exchange(fd_, invalid_socket);
|
||||||
|
}
|
||||||
|
void reset(socket_t fd = invalid_socket) noexcept {
|
||||||
|
if(fd_ != fd) {
|
||||||
|
close(fd_);
|
||||||
|
fd_ = fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const noexcept {
|
||||||
|
return fd_ != invalid_socket;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// this function is a little hack just to fix RSUP-1413
|
||||||
|
// When loging to rublon from withing the host itself, there is no IP delivered through any 'pam_item'
|
||||||
|
// but we need to log from what place the request did came.
|
||||||
|
tl::expected< std::pmr::string, Error > getDefaultRouteIp(std::pmr::memory_resource * mr) {
|
||||||
|
log(LogLevel::Debug, "Reading default route IP");
|
||||||
|
unique_fd sock(::socket(AF_INET, SOCK_DGRAM, 0));
|
||||||
|
if(!sock){
|
||||||
|
log(LogLevel::Warning, "Cant create socket");
|
||||||
|
return tl::unexpected{Error{RublonRuntimeException::CantCreateSocket}};
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr_in remote{};
|
||||||
|
remote.sin_family = AF_INET;
|
||||||
|
remote.sin_port = htons(53);
|
||||||
|
::inet_pton(AF_INET, "8.8.8.8", &remote.sin_addr);
|
||||||
|
|
||||||
|
// we use UDP so no actual connection is estamblished. Only trying to get an interface
|
||||||
|
if(::connect(sock.get(), ( struct sockaddr * ) &remote, sizeof(remote)) < 0) {
|
||||||
|
log(LogLevel::Warning, "Cant connect to socket");
|
||||||
|
return tl::unexpected{Error{RublonRuntimeException::ConnectionFailed}};
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr_in local{};
|
||||||
|
socklen_t len = sizeof(local);
|
||||||
|
if(::getsockname(sock.get(), ( struct sockaddr * ) &local, &len) < 0) {
|
||||||
|
log(LogLevel::Warning, "Cant get socket name");
|
||||||
|
return tl::unexpected{Error{RublonRuntimeException::CantGetSocketName}};
|
||||||
|
}
|
||||||
|
|
||||||
|
char ip[INET_ADDRSTRLEN];
|
||||||
|
::inet_ntop(AF_INET, &local.sin_addr, ip, sizeof(ip));
|
||||||
|
return std::pmr::string{ip, mr};
|
||||||
|
}
|
||||||
|
} // namespace rublon
|
||||||
@ -6,7 +6,7 @@
|
|||||||
namespace rublon {
|
namespace rublon {
|
||||||
namespace memory {
|
namespace memory {
|
||||||
namespace literals {
|
namespace literals {
|
||||||
constexpr std::uint64_t operator"" _kB(unsigned long long kilobytes) {
|
constexpr std::uint64_t operator""_kB(unsigned long long kilobytes) {
|
||||||
return kilobytes * 1024ULL;
|
return kilobytes * 1024ULL;
|
||||||
}
|
}
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|||||||
@ -185,7 +185,7 @@ class MethodSelect {
|
|||||||
for(const auto & method : _methodsAvailable) {
|
for(const auto & method : _methodsAvailable) {
|
||||||
if(method == "totp") {
|
if(method == "totp") {
|
||||||
// skipped in non interactive
|
// skipped in non interactive
|
||||||
if(not _session.inInteractiveMode()) {
|
if(_session.inNonInteractiveMode()) {
|
||||||
logMethodSkippedInteractive(method);
|
logMethodSkippedInteractive(method);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ class MethodSelect {
|
|||||||
|
|
||||||
if(method == "yotp") {
|
if(method == "yotp") {
|
||||||
// skipped in non interactive
|
// skipped in non interactive
|
||||||
if(not _session.inInteractiveMode()) {
|
if(_session.inNonInteractiveMode()) {
|
||||||
logMethodSkippedInteractive(method);
|
logMethodSkippedInteractive(method);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -223,7 +223,7 @@ class MethodSelect {
|
|||||||
|
|
||||||
if(method == "sms") {
|
if(method == "sms") {
|
||||||
// skipped in non interactive
|
// skipped in non interactive
|
||||||
if(not _session.inInteractiveMode()) {
|
if(_session.inNonInteractiveMode()) {
|
||||||
logMethodSkippedInteractive(method);
|
logMethodSkippedInteractive(method);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,5 +31,8 @@ class NonOwningPtr {
|
|||||||
constexpr const T * operator->() const {
|
constexpr const T * operator->() const {
|
||||||
return get();
|
return get();
|
||||||
}
|
}
|
||||||
|
constexpr operator bool() const {
|
||||||
|
return object != nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace rublon
|
} // namespace rublon
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <security/_pam_types.h>
|
|
||||||
#include <security/pam_appl.h>
|
#include <security/pam_appl.h>
|
||||||
#include <security/pam_client.h>
|
|
||||||
#include <security/pam_ext.h>
|
#include <security/pam_ext.h>
|
||||||
#include <security/pam_misc.h>
|
|
||||||
#include <security/pam_modules.h>
|
|
||||||
|
|
||||||
#include <rublon/non_owning_ptr.hpp>
|
#include <rublon/non_owning_ptr.hpp>
|
||||||
#include <rublon/utils.hpp>
|
#include <rublon/utils.hpp>
|
||||||
@ -14,9 +10,15 @@ namespace rublon {
|
|||||||
class LinuxPam {
|
class LinuxPam {
|
||||||
pam_handle_t * pamh;
|
pam_handle_t * pamh;
|
||||||
|
|
||||||
|
bool _noninteractive{false};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LinuxPam(pam_handle_t * handler) : pamh{handler} {}
|
LinuxPam(pam_handle_t * handler) : pamh{handler} {}
|
||||||
|
|
||||||
|
void enableNoninteractive(){
|
||||||
|
_noninteractive = true;
|
||||||
|
}
|
||||||
|
|
||||||
rublon::NonOwningPtr< const char > ip() const {
|
rublon::NonOwningPtr< const char > ip() const {
|
||||||
const void * ip = NULL;
|
const void * ip = NULL;
|
||||||
pam_get_item(pamh, PAM_RHOST, &ip);
|
pam_get_item(pamh, PAM_RHOST, &ip);
|
||||||
@ -39,6 +41,10 @@ class LinuxPam {
|
|||||||
|
|
||||||
template < typename... Ti >
|
template < typename... Ti >
|
||||||
void print(const char * fmt, Ti... ti) const noexcept {
|
void print(const char * fmt, Ti... ti) const noexcept {
|
||||||
|
if(_noninteractive){
|
||||||
|
log(LogLevel::Info, "pam_print ommited due working in noninteractive mode");
|
||||||
|
return;
|
||||||
|
}
|
||||||
char buf[256] = {};
|
char buf[256] = {};
|
||||||
sprintf(buf, fmt, std::forward< Ti >(ti)...);
|
sprintf(buf, fmt, std::forward< Ti >(ti)...);
|
||||||
if(auto r = pam_prompt(pamh, PAM_TEXT_INFO, nullptr, fmt, std::forward< Ti >(ti)...); r != PAM_SUCCESS) {
|
if(auto r = pam_prompt(pamh, PAM_TEXT_INFO, nullptr, fmt, std::forward< Ti >(ti)...); r != PAM_SUCCESS) {
|
||||||
@ -48,6 +54,7 @@ class LinuxPam {
|
|||||||
|
|
||||||
template < typename Fun, typename... Ti >
|
template < typename Fun, typename... Ti >
|
||||||
auto scan(Fun && f, const char * fmt, Ti... ti) const noexcept {
|
auto scan(Fun && f, const char * fmt, Ti... ti) const noexcept {
|
||||||
|
assert(_noninteractive == false);
|
||||||
char * response = nullptr;
|
char * response = nullptr;
|
||||||
pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &response, fmt, std::forward< Ti >(ti)...);
|
pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &response, fmt, std::forward< Ti >(ti)...);
|
||||||
if(response) {
|
if(response) {
|
||||||
|
|||||||
@ -42,5 +42,9 @@ class PamStub {
|
|||||||
}
|
}
|
||||||
return std::result_of_t< Fun(char *) >();
|
return std::result_of_t< Fun(char *) >();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enableNoninteractive(){
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace rublon
|
} // namespace rublon
|
||||||
|
|||||||
@ -12,17 +12,27 @@ namespace rublon {
|
|||||||
|
|
||||||
class RublonFactory {
|
class RublonFactory {
|
||||||
public:
|
public:
|
||||||
tl::expected< void, Error > initializeSession(Session & session) {
|
tl::expected< void, Error > initializeSession(Session & session, [[maybe_unused]]int flags, int argc, const char ** argv) {
|
||||||
log(LogLevel::Debug, "Configuration read start");
|
log(LogLevel::Debug, "Configuration read start");
|
||||||
memory::MonotonicStack_2k_Resource memory_resource;
|
memory::MonotonicStack_2k_Resource memory_resource;
|
||||||
ConfigurationReader reader{&memory_resource, "/etc/rublon.config"};
|
ConfigurationReader reader{&memory_resource, "/etc/rublon.config", argc, argv};
|
||||||
|
|
||||||
if(auto ok = reader.applyTo(session.config()); not ok.has_value()) {
|
if(auto ok = reader.applyTo(session.config()); not ok) {
|
||||||
log(LogLevel::Warning, "Configuration contains errors");
|
log(LogLevel::Warning, "Configuration contains errors");
|
||||||
session.pam().print("The configuration file does not exist or contains incorrect values");
|
session.pam().print("The configuration file does not exist or contains incorrect values");
|
||||||
return tl::unexpected{ConfigurationError{}};
|
return tl::unexpected{ConfigurationError{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
log(LogLevel::Debug, "Configuration read success");
|
log(LogLevel::Debug, "Configuration read success");
|
||||||
|
|
||||||
|
// if(flags & PAM_SILENT) {
|
||||||
|
// log(LogLevel::Debug, "nonInteractiveMode enabled (PAM_SILENT flag is set)");
|
||||||
|
// session.switchToNonInteractiveMode();
|
||||||
|
// }
|
||||||
|
|
||||||
|
if(session.inNonInteractiveMode())
|
||||||
|
session.updateInteractiveFlag();
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,16 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <rublon/memory.hpp>
|
#include <rapidjson/document.h>
|
||||||
#include <rublon/bits.hpp>
|
#include <rublon/bits.hpp>
|
||||||
#include <rublon/configuration.hpp>
|
#include <rublon/configuration.hpp>
|
||||||
#include <rublon/json.hpp>
|
#include <rublon/json.hpp>
|
||||||
|
#include <rublon/memory.hpp>
|
||||||
#include <rublon/utils.hpp>
|
#include <rublon/utils.hpp>
|
||||||
#include <rapidjson/document.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace rublon {
|
namespace rublon {
|
||||||
|
|
||||||
|
|
||||||
// class LoggingMemoryResource : public std::pmr::memory_resource {
|
// class LoggingMemoryResource : public std::pmr::memory_resource {
|
||||||
// public:
|
// public:
|
||||||
// LoggingMemoryResource(std::pmr::memory_resource* upstream = std::pmr::get_default_resource())
|
// LoggingMemoryResource(std::pmr::memory_resource* upstream = std::pmr::get_default_resource())
|
||||||
@ -93,13 +91,13 @@ class DefaultResource {
|
|||||||
|
|
||||||
class Session : public DefaultResource {
|
class Session : public DefaultResource {
|
||||||
std::pmr::memory_resource * mr;
|
std::pmr::memory_resource * mr;
|
||||||
const Pam_t & _pam;
|
Pam_t & _pam;
|
||||||
Configuration _config;
|
Configuration _config;
|
||||||
std::pmr::string _tid;
|
std::pmr::string _tid;
|
||||||
std::pmr::string _accessToken;
|
std::pmr::string _accessToken;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Session(const Pam_t & pam) : DefaultResource{}, mr{memory::default_resource()}, _pam{pam}, _config{mr}, _tid{mr}, _accessToken{mr} {
|
Session(Pam_t & pam) : DefaultResource{}, mr{memory::default_resource()}, _pam{pam}, _config{mr}, _tid{mr}, _accessToken{mr} {
|
||||||
details::initLog();
|
details::initLog();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,13 +107,16 @@ class Session : public DefaultResource {
|
|||||||
Session & operator=(Session &&) noexcept = delete;
|
Session & operator=(Session &&) noexcept = delete;
|
||||||
Session & operator=(const Session &) = delete;
|
Session & operator=(const Session &) = delete;
|
||||||
|
|
||||||
const auto & pam() const {
|
constexpr const auto & pam() const {
|
||||||
return _pam;
|
return _pam;
|
||||||
}
|
}
|
||||||
auto & config() {
|
constexpr auto & pam() {
|
||||||
|
return _pam;
|
||||||
|
}
|
||||||
|
constexpr auto & config() {
|
||||||
return _config;
|
return _config;
|
||||||
}
|
}
|
||||||
const auto & config() const {
|
constexpr const auto & config() const {
|
||||||
return _config;
|
return _config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +127,21 @@ class Session : public DefaultResource {
|
|||||||
return systemToken().data();
|
return systemToken().data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void switchToNonInteractiveMode(){
|
||||||
|
if(inInteractiveMode()) {
|
||||||
|
log(LogLevel::Debug, "Switched to non interactive mode");
|
||||||
|
_config.nonInteractiveMode = true;
|
||||||
|
_pam.enableNoninteractive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// updates PAM configuration based on nonInteractiveMode flag in configuration
|
||||||
|
void updateInteractiveFlag(){
|
||||||
|
if(_config.nonInteractiveMode){
|
||||||
|
_pam.enableNoninteractive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constexpr bool inNonInteractiveMode() const {
|
constexpr bool inNonInteractiveMode() const {
|
||||||
return not inInteractiveMode();
|
return not inInteractiveMode();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -228,13 +228,12 @@ namespace details {
|
|||||||
|
|
||||||
std::pmr::string hostname(std::pmr::memory_resource * mr) {
|
std::pmr::string hostname(std::pmr::memory_resource * mr) {
|
||||||
// longest hostname on linux is 253 characters
|
// longest hostname on linux is 253 characters
|
||||||
std::pmr::string hostname{255, '\0', mr};
|
char host[255]{};
|
||||||
if(gethostname(hostname.data(), hostname.size()) != 0) {
|
if(gethostname(host, sizeof(host)) != 0) {
|
||||||
log(LogLevel::Warning, "Hostname is not available");
|
log(LogLevel::Warning, "Hostname is not available");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
hostname.resize(hostname.find_first_of('\0'));
|
return {host, mr};
|
||||||
return hostname;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pmr::string osName(std::pmr::memory_resource * mr) {
|
std::pmr::string osName(std::pmr::memory_resource * mr) {
|
||||||
|
|||||||
@ -1,10 +1,3 @@
|
|||||||
#include <security/pam_appl.h>
|
|
||||||
#include <security/pam_client.h>
|
|
||||||
#include <security/pam_ext.h>
|
|
||||||
#include <security/pam_misc.h>
|
|
||||||
#include <security/pam_modules.h>
|
|
||||||
#include <syslog.h>
|
|
||||||
|
|
||||||
#include <rublon/check_application.hpp>
|
#include <rublon/check_application.hpp>
|
||||||
#include <rublon/error.hpp>
|
#include <rublon/error.hpp>
|
||||||
#include <rublon/error_handler.hpp>
|
#include <rublon/error_handler.hpp>
|
||||||
@ -29,13 +22,12 @@ DLL_PUBLIC int pam_sm_acct_mgmt([[maybe_unused]] pam_handle_t * pamh,
|
|||||||
}
|
}
|
||||||
|
|
||||||
DLL_PUBLIC int
|
DLL_PUBLIC int
|
||||||
pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unused]] int argc, [[maybe_unused]] const char ** argv) {
|
pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char ** argv) {
|
||||||
using namespace rublon;
|
using namespace rublon;
|
||||||
details::initLog();
|
details::initLog();
|
||||||
LinuxPam pam{pamh};
|
LinuxPam pam{pamh};
|
||||||
|
|
||||||
log(LogLevel::Info, "user '%s' authentication attempt", pam.username().get());
|
log(LogLevel::Info, "user '%s' authentication attempt", pam.username().get());
|
||||||
|
|
||||||
auto printAuthMessageAndExit = [&](const AuthenticationStatus status) {
|
auto printAuthMessageAndExit = [&](const AuthenticationStatus status) {
|
||||||
switch(status.action()) {
|
switch(status.action()) {
|
||||||
case AuthenticationStatus::Action::Bypass:
|
case AuthenticationStatus::Action::Bypass:
|
||||||
@ -59,7 +51,7 @@ pam_sm_authenticate(pam_handle_t * pamh, [[maybe_unused]] int flags, [[maybe_unu
|
|||||||
};
|
};
|
||||||
|
|
||||||
Session session{pam};
|
Session session{pam};
|
||||||
auto ok = rublon::RublonFactory{}.initializeSession(session);
|
auto ok = rublon::RublonFactory{}.initializeSession(session, flags, argc, argv);
|
||||||
if(not ok.has_value()) {
|
if(not ok.has_value()) {
|
||||||
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,14 +9,14 @@ if [ ! -f $RUBLON_CONFIG ]
|
|||||||
then
|
then
|
||||||
cp -a /usr/share/rublon/rublon.config.defaults $RUBLON_CONFIG
|
cp -a /usr/share/rublon/rublon.config.defaults $RUBLON_CONFIG
|
||||||
chown root:root $RUBLON_CONFIG
|
chown root:root $RUBLON_CONFIG
|
||||||
chmod 640 $RUBLON_CONFIG
|
chmod 644 $RUBLON_CONFIG
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f $RUBLON_SSH_CONFIG ]
|
if [ ! -f $RUBLON_SSH_CONFIG ]
|
||||||
then
|
then
|
||||||
cp -a /usr/share/rublon/01-rublon-ssh.conf.default $RUBLON_SSH_CONFIG
|
cp -a /usr/share/rublon/01-rublon-ssh.conf.default $RUBLON_SSH_CONFIG
|
||||||
chown root:root $RUBLON_SSH_CONFIG
|
chown root:root $RUBLON_SSH_CONFIG
|
||||||
chmod 640 $RUBLON_SSH_CONFIG
|
chmod 644 $RUBLON_SSH_CONFIG
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f /etc/os-release ]
|
if [ -f /etc/os-release ]
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
auth required pam_env.so
|
auth required pam_env.so
|
||||||
auth requisite pam_rublon.so
|
auth requisite pam_unix.so
|
||||||
|
auth sufficient pam_rublon.so
|
||||||
auth required pam_deny.so
|
auth required pam_deny.so
|
||||||
Loading…
Reference in New Issue
Block a user