* Fix log file access, refactor configuration reading class * Remove bypass option in favor of failmode * fix loging, print enrolment info * Add EMAIL method * Add yubi authentication method * Add support for verification message * Add verification * Made changes in Vagrant's files to run different OSs * Switch off tests and packages demands to run PAM on Debian 11 * Add authentication totp * Changes in utils * Remove unnessesary interface * Changed vagrant files and postinstal script for Ubuntu 20 and 22 * Moved adding PasswordAuth to vagrant file from posinst * Added ubuntu 24.04 * Set version * Poprawki UI * WebSocket implementation * Add totp authentication method * fixup changes in utils * Remove unnessesary interface and simplify code * Remove "default" message handler from WebSocket class * Change display names of known authentication methods * Cleanup code in 'main' file * Add CheckApplication * Remove unused function * Changed vagrant files and postinstal script for Ubuntu 20 and 22 * Moved adding PasswordAuth to vagrant file from posinst * Added ubuntu 24.04 * Set version to 2.0.2 * Proper handle for missing configuration * Fixup use value of optional object * Add more vCPU/RAM to vagrant VM's + fix translations * Minor WS fixes, translations * Proper handler for Werification error * Make use of prompt parameter * Add max number of prompts * remove unused code, fir includes * Add Waiting status * Add check application status check --------- Co-authored-by: Madzik <m.w@linux.pl>
181 lines
8.2 KiB
C++
Executable File
181 lines
8.2 KiB
C++
Executable File
#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/error.hpp>
|
|
#include <rublon/finish.hpp>
|
|
#include <rublon/rublon.hpp>
|
|
#include <rublon/utils.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;
|
|
}
|
|
|
|
std::string g_tid;
|
|
|
|
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;
|
|
details::initLog();
|
|
LinuxPam pam{pamh};
|
|
|
|
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;
|
|
};
|
|
|
|
auto session = rublon::RublonFactory{}.startSession(pam);
|
|
if(not session.has_value()) {
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
|
}
|
|
|
|
auto & CH = session.value().coreHandler();
|
|
|
|
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(CH, pam); };
|
|
|
|
auto finalizeTransaction = [&](const AuthenticationStatus & status) -> tl::expected< AuthenticationStatus, Error > {
|
|
if(status.userAuthorized()) {
|
|
auto tok = std::string{status.accessToken().substr(10, 60).data(), 60};
|
|
|
|
Finish finish{session.value().config(), std::move(tok)};
|
|
finish.handle(CH);
|
|
}
|
|
return status;
|
|
};
|
|
|
|
auto allowLogin = [&](const AuthenticationStatus & status) -> tl::expected< int, Error > { return printAuthMessageAndExit(status); };
|
|
|
|
auto mapError = [&](const Error & error) -> tl::expected< int, Error > {
|
|
log(LogLevel::Error, "Process interrupted by {%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:
|
|
pam.print("Access denied! Contact your administrator for more information");
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
|
case RublonAuthenticationInterrupt::ErrorClass::UserWaiting:
|
|
case RublonAuthenticationInterrupt::ErrorClass::UserPending:
|
|
pam.print(
|
|
"Your account is awaiting administrator's approval. \n"
|
|
"Contact your administrator and ask them to approve your account");
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
|
case RublonAuthenticationInterrupt::ErrorClass::UserNotFound:
|
|
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(session.value().config().failMode == FailMode::deny) {
|
|
pam.print("Incorrect response from the Rublon API, user bypassed");
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
|
} else {
|
|
pam.print("Incorrect response from the Rublon API, user access denied");
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
|
}
|
|
}
|
|
|
|
if(error.is< CoreHandlerError >()) {
|
|
const auto & reson = error.get< CoreHandlerError >().reson;
|
|
pam.print("Something went wrong and authentication could not be completed, contact your administrator");
|
|
|
|
if(reson == "UserBypassedException" or reson == "UserNotFoundException")
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Bypass);
|
|
}
|
|
|
|
if(error.is< WerificationError >()) {
|
|
switch(error.get< WerificationError >().errorClass) {
|
|
case WerificationError::ErrorClass::PasscodeException:
|
|
pam.print(R"(Incorrect passcode)");
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
|
case WerificationError::ErrorClass::BadInput:
|
|
pam.print(R"(Ensure that the Secret Key is correct.)");
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
|
}
|
|
}
|
|
|
|
if(error.is< RublonCheckApplicationException >()) {
|
|
switch(error.get< RublonCheckApplicationException >().errorClass) {
|
|
case RublonCheckApplicationException::ErrorClass::ApplicationNotFoundException:
|
|
log(LogLevel::Error, R"(Could not find the application in the Rublon Admin Console.)");
|
|
log(LogLevel::Error, R"(Ensure that the application exists and the SystemToken is correct.)");
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
|
case RublonCheckApplicationException::ErrorClass::InvalidSignatureException:
|
|
log(LogLevel::Error, R"(Could not verify the signature.)");
|
|
log(LogLevel::Error, R"(Ensure that the Secret Key is correct.)");
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
|
case RublonCheckApplicationException::ErrorClass::UnsupportedVersionException:
|
|
log(LogLevel::Error, R"(The provided version of the app is unsupported.)");
|
|
log(LogLevel::Error, R"(Try changing the app version.)");
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
|
}
|
|
}
|
|
|
|
return printAuthMessageAndExit(AuthenticationStatus::Action::Denied);
|
|
};
|
|
|
|
{
|
|
CheckApplication ca;
|
|
auto ret =
|
|
ca.call(CH, {session.value().config().systemToken.data(), session.value().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()}
|
|
.handle(CH, pam) //
|
|
.and_then(selectMethod)
|
|
.and_then(confirmMethod)
|
|
.and_then(confirmCode)
|
|
.and_then(finalizeTransaction)
|
|
.and_then(allowLogin)
|
|
.or_else(mapError);
|
|
|
|
return ret.value_or(PAM_MAXTRIES);
|
|
}
|