rublon-ssh/PAM/ssh/lib/pam.cpp
rublon-bwi 8ffa20fffa
Bwi/sms link (#8)
* generate user enrolement message

* cleanup

* Fix bugs found during testing

* Add yotp message [not verified]

* smsLink implementation

* implement SMS Link

* YOTP fixes

* Add SMS link
2024-02-13 16:50:45 +01:00

130 lines
5.3 KiB
C++

#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/rublon.hpp>
#define DLL_PUBLIC __attribute__((visibility("default")))
DLL_PUBLIC int pam_sm_setcred([[maybe_unused]] pam_handle_t * pamh,
[[maybe_unused]] int flags,
[[maybe_unused]] int argc,
[[maybe_unused]] const char ** argv) {
return PAM_SUCCESS;
}
DLL_PUBLIC int pam_sm_acct_mgmt([[maybe_unused]] pam_handle_t * pamh,
[[maybe_unused]] int flags,
[[maybe_unused]] int argc,
[[maybe_unused]] const char ** argv) {
return PAM_SUCCESS;
}
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;
// std::freopen(rublon::details::logPath(), "a+", stdout);
LinuxPam pam{pamh};
// std::freopen(rublon::details::logPath(), "a+", stderr);
auto config = ConfigurationFactory{}.systemConfig();
if(not config.has_value()) {
pam.print("\n");
pam.print("Rublon configuration does not exists or is invalid");
pam.print("\tcheck '%s' for more details\n", details::logPath());
return PAM_SUCCESS;
}
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);
CoreHandler CH{*config};
auto selectMethod = [&](const MethodSelect & selector) { return selector.create(pam); };
auto confirmMethod = [&](const PostMethod & confirm) { return confirm.fire(CH); };
auto confirmCode = [&](const MethodProxy & method) { return method.fire(CH, pam); };
auto printAuthMessageAndExit = [&](const AuthenticationStatus status) {
switch(status.action()) {
case AuthenticationStatus::Action::Bypass:
pam.print("\n\tRUBLON authentication bypased");
return PAM_SUCCESS;
case AuthenticationStatus::Action::Denied:
pam.print("\n\tRUBLON authentication FAILED");
return PAM_MAXTRIES;
case AuthenticationStatus::Action::Confirmed:
pam.print("\n\tRUBLON authentication SUCCESS");
return PAM_SUCCESS;
}
pam.print("RUBLON connector has exited with unknown code, access DENY!\n");
return PAM_MAXTRIES;
};
auto allowLogin = [&](const AuthenticationStatus & status) -> tl::expected< int, Error > {
return printAuthMessageAndExit(status);
};
auto mapError = [&](const Error & error) -> tl::expected< int, Error > {
log(LogLevel::Error, "Authorization interrupted with {%s::%s}", error.errorClassName(), error.categoryName());
if(error.is< RublonAuthenticationInterrupt >()) {
switch(error.get< RublonAuthenticationInterrupt >().errorClass) {
case RublonAuthenticationInterrupt::ErrorClass::UserBaypass:
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
case RublonAuthenticationInterrupt::ErrorClass::UserDenied:
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
case RublonAuthenticationInterrupt::ErrorClass::UserPending:
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
}
}
if(error.is< MethodError >()) {
switch(error.get< MethodError >().errorClass) {
case MethodError::ErrorClass::BadMethod:
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
case MethodError::ErrorClass::BadUserInput:
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
case MethodError::ErrorClass::NoMethodAvailable:
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
}
}
if(error.is< ConnectionError >()) {
if(config->bypass) {
pam.print("Connection Error, bypass enabled");
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
} else {
pam.print("Connection Error, bypass disabled");
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
}
}
if(error.is< CoreHandlerError >()) {
const auto &reson = error.get< CoreHandlerError >().reson;
pam.print("\n RUBLON server returned '%s' exception", reson.c_str());
if(reson == "UserBypassedException" or reson == "UserNotFoundException")
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
}
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
};
auto ret = Init{config.value()}
.fire(CH, pam) //
.and_then(selectMethod)
.and_then(confirmMethod)
.and_then(confirmCode)
.and_then(allowLogin)
.or_else(mapError);
return ret.value_or(PAM_MAXTRIES);
}