From: John Turner Date: Thu, 24 Jul 2025 07:58:01 +0000 (-0400) Subject: init X-Git-Tag: 0.0.1 X-Git-Url: https://jturnerusa.dev/gitweb/?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=pam_xdg init --- 432cd2fe2f2e33b2cc8bec60246ea59b7e2210f9 diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..a08f21b --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,13 @@ +((c++-ts-mode + . ((fmt-executable . "clang-format") + (eval . (setq-local fmt-args `("--assume-filename" ,(buffer-file-name)))) + (eval . (add-hook 'before-save-hook 'fmt-current-buffer nil t)) + (eval . (add-to-list 'eglot-server-programs '(c++-mode . ("clangd" "-header-insertion=never" "-clang-tidy")))) + (eval . (eglot-ensure)) + (eval . (add-hook 'eglot-managed-mode-hook (lambda () + (eglot-inlay-hints-mode -1)))) + (eval . (company-mode 1)))) + (meson-mode + . ((fmt-executable . "meson") + (fmt-args . ("format" "-")) + (eval . (add-hook 'before-save-hook 'fmt-current-buffer nil t))))) diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..2960abd --- /dev/null +++ b/meson.build @@ -0,0 +1,24 @@ +project( + 'pam_xdg', + 'cpp', + version: '0.0.1', + meson_version: '>=1.4.0', + default_options: ['warning_level=3', 'cpp_std=c++23'], +) + +pam = dependency('pam') +sqlite3 = dependency('sqlite3') +sqlitecpp = dependency('sqlitecpp') + +sources = files('src/pam_xdg.cpp') + +libdir = get_option('libdir') + +pam_xdg = shared_library( + 'pam_xdg', + sources, + dependencies: [pam, sqlite3, sqlitecpp], + install: true, + install_dir: libdir / 'security', + name_prefix: '', +) diff --git a/src/pam_xdg.cpp b/src/pam_xdg.cpp new file mode 100644 index 0000000..de2d41f --- /dev/null +++ b/src/pam_xdg.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +const std::filesystem::path DB_PATH = "/run/xdg_pam.db"; +const std::filesystem::path RUNTIME_DIRECTORY = "/run/user/"; + +namespace pam_xdg { + +void create_directory(const std::filesystem::path &path, mode_t chmod, + int owner) { + if (mkdir(path.c_str(), chmod) != 0) { + throw std::runtime_error(std::format("failed to create directory {}: {}", + path.c_str(), std::strerror(errno))); + } + + if (chown(path.c_str(), owner, owner) != 0) { + throw std::runtime_error( + std::format("failed to chown directory {}", path.c_str())); + } +} + +void create_table(SQLite::Database &db) { + db.exec("CREATE TABLE IF NOT EXISTS SESSIONS(USERNAME STRING, LOGINS INT)"); +} + +void create_session(SQLite::Database &db, const std::string &username) { + SQLite::Statement stmt( + db, "INSERT OR IGNORE INTO SESSIONS(USERNAME, LOGINS) VALUES(?, 0)"); + + stmt.bind(1, username); + + stmt.exec(); +} + +void login(SQLite::Database &db, const std::string &username) { + SQLite::Statement stmt( + db, "UPDATE SESSIONS SET LOGINS=LOGINS+1 WHERE USERNAME=?"); + + stmt.bind(1, username); + + stmt.exec(); +} + +void logout(SQLite::Database &db, const std::string &username) { + SQLite::Statement stmt( + db, "UPDATE SESSIONS SET LOGINS=LOGINS-1 WHERE USERNAME=?"); + + stmt.bind(1, username); + + stmt.exec(); +} + +bool is_logged_in(SQLite::Database &db, const std::string &username) { + SQLite::Statement stmt(db, "SELECT LOGINS FROM SESSIONS WHERE USERNAME=?"); + + stmt.bind(1, username); + + stmt.executeStep(); + + int logins = stmt.getColumn(0); + + return (logins > 0); +} +} // namespace pam_xdg + +extern "C" int pam_sm_open_session(pam_handle_t *pam, [[gnu::unused]] int flags, + [[gnu::unused]] int argc, + [[gnu::unused]] const char **argv) { + + const void *data; + pam_get_item(pam, PAM_USER, &data); + auto *username = static_cast(data); + + if (std::string_view(username) == "root") { + return 0; + } + + auto passwd = getpwnam(username); + auto uid = passwd->pw_uid; + + auto user_directory = RUNTIME_DIRECTORY / std::format("{}", uid); + + try { + SQLite::Database db(DB_PATH, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); + + pam_xdg::create_table(db); + pam_xdg::create_session(db, username); + + if (!pam_xdg::is_logged_in(db, username)) { + if (!std::filesystem::exists(RUNTIME_DIRECTORY)) { + pam_xdg::create_directory(RUNTIME_DIRECTORY, 0755, 0); + } + + pam_xdg::create_directory(user_directory, 0700, uid); + } + + pam_xdg::login(db, username); + + auto envvar = std::format("XDG_RUNTIME_DIR={}", user_directory.c_str()); + + if (pam_putenv(pam, envvar.c_str()) != PAM_SUCCESS) { + throw std::runtime_error( + std::string("failed to set XDG_RUNTIME_DIR environment variable")); + } + } catch (std::exception &e) { + std::print("{}", e.what()); + return PAM_SESSION_ERR; + } + + return PAM_SUCCESS; +} + +extern "C" int pam_sm_close_session(pam_handle_t *pam, + [[gnu::unused]] int flags, + [[gnu::unused]] int argc, + [[gnu::unused]] const char **argv) { + + const void *data; + pam_get_item(pam, PAM_USER, &data); + auto *username = static_cast(data); + + if (std::string_view(username) == "root") { + return 0; + } + + auto passwd = getpwnam(username); + auto uid = passwd->pw_uid; + + auto user_directory = std::filesystem::path( + std::filesystem::path(RUNTIME_DIRECTORY) / std::format("{}", uid)); + + try { + SQLite::Database db(DB_PATH, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE); + + pam_xdg::logout(db, username); + + if (!pam_xdg::is_logged_in(db, username)) { + std::filesystem::remove_all(user_directory); + } + + } catch (std::exception &e) { + std::print("{}", e.what()); + return PAM_SESSION_ERR; + } + + return PAM_SUCCESS; +}