rublon-ssh/PAM/ssh/include/rublon/utils.hpp
rublon-bwi 9415174eba
Bwi/bugfix round2 (#9)
* 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>
2024-05-28 12:04:20 +02:00

289 lines
8.0 KiB
C++
Executable File

#pragma once
#include "tl/expected.hpp"
#include <algorithm>
#include <array>
#include <cctype>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <memory_resource>
#include <sstream>
#include <string_view>
#include <alloca.h>
#include <fcntl.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <sys/stat.h>
#include <syslog.h>
#include <unistd.h>
#include <utility>
#include <rublon/memory.hpp>
namespace rublon {
inline bool fileGood(const std::filesystem::path & path) {
std::ifstream file(path);
return file.good();
}
template < typename T >
inline bool readFile(const std::filesystem::path & path, T & destination) {
if(not fileGood(path))
return false;
std::ifstream file(path);
file.seekg(0, std::ios::end);
size_t size = file.tellg();
destination.resize(size);
file.seekg(0);
file.read(&destination[0], size);
return true;
}
enum class LogLevel { Debug, Info, Warning, Error };
inline auto dateStr() {
std::array< char, 32 > date;
time_t now;
time(&now);
strftime(date.data(), date.size(), "%FT%TZ", gmtime(&now));
return date;
}
constexpr const char * LogLevelNames[]{"Debug", "Info", "Warning", "Error"};
LogLevel g_level = LogLevel::Debug;
constexpr bool syncLogFile = true;
static const char * application = "";
// #include <openssl/md5.h>
// #include <sys/types.h>
// #include <sys/stat.h>
// #include <sys/mman.h>
// unsigned char result[MD5_DIGEST_LENGTH];
// // Print the MD5 sum as hex-digits.
// void print_md5_sum(unsigned char* md) {
// int i;
// for(i=0; i <MD5_DIGEST_LENGTH; i++) {
// printf("%02x",md[i]);
// }
// }
// // Get the size of the file by its file descriptor
// unsigned long get_size_by_fd(int fd) {
// struct stat statbuf;
// if(fstat(fd, &statbuf) < 0) exit(-1);
// return statbuf.st_size;
// }
// int md5(const char*filename) {
// int file_descript;
// unsigned long file_size;
// char* file_buffer;
// file_descript = open(filename, O_RDONLY);
// if(file_descript < 0) exit(-1);
// file_size = get_size_by_fd(file_descript);
// printf("file size:\t%lu\n", file_size);
// file_buffer =(char*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, file_descript, 0);
// MD5((unsigned char*) file_buffer, file_size, result);
// munmap(file_buffer, file_size);
// return 0;
// }
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;
}
inline void touch(const char * filename) {
close(open(filename, O_CREAT | O_RDWR, 0640));
}
inline void mkdir(const char * dirname) {
::mkdir(dirname, O_CREAT | O_RDWR);
}
inline bool exists(const char * filename) {
return access(filename, F_OK) == 0;
}
inline bool logMissing() noexcept {
return not exists(logPath());
}
inline const char * initLog(const char * app = nullptr) noexcept {
if(logMissing()) {
touch(logPath());
}
if(not app)
application = app;
return logPath();
}
inline void doLog(LogLevel level, const char * line) noexcept {
auto fp = std::unique_ptr< FILE, int (*)(FILE *) >(fopen(initLog(), "a+"), fclose);
if(fp) {
/// TODO add transaction ID
fprintf(
fp.get(), "%s %s[%s] %s\n", dateStr().data(), application == nullptr ? "" : application, LogLevelNames[( int ) level], line);
if(syncLogFile)
sync();
}
// openlog ("auth", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_AUTH);
// syslog (LOG_INFO, "[%s] %s", "pam_rublon", line);
// closelog ();
}
} // namespace details
inline void log(LogLevel level, const char * line) noexcept {
if(level < g_level)
return;
details::doLog(level, line);
}
template < typename... Ti >
void log(LogLevel level, const char * fmt, Ti &&... ti) noexcept {
if(level < g_level)
return;
constexpr auto maxEntryLength = 1000;
std::array< char, maxEntryLength > line;
snprintf(line.data(), maxEntryLength, fmt, std::forward< Ti >(ti)...);
details::doLog(level, line.data());
}
class PrintUser {
public:
template < typename Printer >
PrintUser(Printer & p) : _impl{std::make_unique< ModelImpl >(p)} {}
private:
struct model {
virtual ~model();
virtual void print(std::string_view line) const = 0;
};
template < typename Printer_T >
struct ModelImpl : public model {
ModelImpl(Printer_T & printer) : _printer{printer} {}
void print(std::string_view line) const override {
_printer.print(line);
}
Printer_T & _printer;
};
std::unique_ptr< model > _impl;
};
namespace conv {
enum class Error { OutOfRange, NotANumber };
inline bool to_bool(std::string_view userinput) {
if(userinput.size() > 16) {
// todo return optional
return false;
}
std::array< char, 16 >buf{};
auto asciitolower = [](char in) { return in - ((in <= 'Z' && in >= 'A') ? ('Z' - 'z') : 0); };
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);
}
}
} // namespace conv
namespace details {
static inline std::string_view ltrim(std::string_view s) {
while(s.length() && std::isspace(*s.begin()))
s.remove_prefix(1);
return s;
}
static inline std::string_view rtrim(std::string_view s) {
while(s.length() && std::isspace(*s.rbegin()))
s.remove_suffix(1);
return s;
}
static inline std::string_view trim(std::string_view s) {
return ltrim(rtrim(s));
}
template < typename Headers >
inline void headers(std::string_view data, Headers & headers) {
memory::StrictMonotonic_4k_HeapResource stackResource;
std::pmr::string tmp{&stackResource};
std::istringstream resp{};
resp.rdbuf()->pubsetbuf(const_cast< char * >(data.data()), data.size());
while(std::getline(resp, tmp) && !(trim(tmp).empty())) {
auto line = std::string_view(tmp);
auto index = tmp.find(':', 0);
if(index != std::string::npos) {
headers.insert({//
typename Headers::key_type{trim(line.substr(0, index)), headers.get_allocator()},
typename Headers::mapped_type{trim(line.substr(index + 1)), headers.get_allocator()}});
}
}
}
} // namespace details
} // namespace rublon