summaryrefslogtreecommitdiff
path: root/src/pam_xdg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pam_xdg.cpp')
-rw-r--r--src/pam_xdg.cpp272
1 files changed, 208 insertions, 64 deletions
diff --git a/src/pam_xdg.cpp b/src/pam_xdg.cpp
index 353f443..b397024 100644
--- a/src/pam_xdg.cpp
+++ b/src/pam_xdg.cpp
@@ -1,83 +1,205 @@
-#include <SQLiteCpp/SQLiteCpp.h>
-#include <pwd.h>
-#include <security/pam_modules.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
#include <cerrno>
#include <cstring>
#include <filesystem>
-#include <format>
#include <print>
+#include <stdexcept>
+#include <string>
+#include <string_view>
-const std::filesystem::path DB_PATH = "/run/pam_xdg.db";
-const std::filesystem::path RUNTIME_DIRECTORY = "/run/user/";
+#include <fcntl.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <security/pam_modules.h>
+#include <sqlite3.h>
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)));
+constexpr const std::string_view USER_DIRECTORY = "/run/user";
+constexpr const std::string_view DB_PATH = "/run/pam_xdg.db";
+
+void open_db(sqlite3 **db, const std::filesystem::path &path, int flags) {
+ auto rc = sqlite3_open_v2(path.c_str(), db, flags, nullptr);
+
+ if (rc != SQLITE_OK) {
+ throw std::runtime_error(std::string("failed to open database"));
}
+}
+
+void init_db(sqlite3 *db) {
+ auto rc = sqlite3_exec(
+ db, "CREATE TABLE IF NOT EXISTS SESSIONS(USERNAME TEXT, LOGINS INT)",
+ nullptr, nullptr, nullptr);
- if (chown(path.c_str(), owner, owner) != 0) {
+ if (rc != SQLITE_OK) {
throw std::runtime_error(
- std::format("failed to chown directory {}", path.c_str()));
+ std::string("failed to init database: {}", sqlite3_errmsg(db)));
}
}
-void create_table(SQLite::Database &db) {
- db.exec("CREATE TABLE IF NOT EXISTS SESSIONS(USERNAME STRING, LOGINS INT)");
-}
+void init_session(sqlite3 *db, std::string_view username) {
+ const auto *query =
+ "INSERT OR IGNORE INTO SESSIONS(USERNAME, LOGINS) VALUES(?, 0)";
+ sqlite3_stmt *stmt;
+ int rc;
-void create_session(SQLite::Database &db, const std::string &username) {
- SQLite::Statement stmt(
- db, "INSERT OR IGNORE INTO SESSIONS(USERNAME, LOGINS) VALUES(?, 0)");
+ rc = sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
- stmt.bind(1, username);
+ if (rc != SQLITE_OK) {
+ throw std::runtime_error(
+ std::format("failed to prepare stmt: {}", sqlite3_errmsg(db)));
+ }
- stmt.exec();
+ rc = sqlite3_bind_text(stmt, 1, username.data(), username.length(), nullptr);
+
+ if (rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ throw std::runtime_error(std::string("failed to bind text to stmt"));
+ }
+
+ rc = sqlite3_step(stmt);
+
+ if (rc != SQLITE_DONE) {
+ sqlite3_finalize(stmt);
+ throw std::runtime_error(std::string("failed to step stmt"));
+ }
+
+ sqlite3_finalize(stmt);
}
-void login(SQLite::Database &db, const std::string &username) {
- SQLite::Statement stmt(
- db, "UPDATE SESSIONS SET LOGINS=LOGINS+1 WHERE USERNAME=?");
+void record_login(sqlite3 *db, std::string_view username) {
+ const auto *query = "UPDATE SESSIONS SET LOGINS=LOGINS+1 WHERE USERNAME=?";
+ sqlite3_stmt *stmt;
+ int rc;
+
+ rc = sqlite3_prepare_v2(db, query, -1, &stmt, nullptr);
- stmt.bind(1, username);
+ if (rc != SQLITE_OK) {
+ throw std::runtime_error(
+ std::string("failed to prepare stmt: {}", sqlite3_errmsg(db)));
+ }
+
+ rc = sqlite3_bind_text(stmt, 1, username.data(), username.length(), nullptr);
+
+ if (rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ throw std::runtime_error(std::string("failed to bind text"));
+ }
- stmt.exec();
+ rc = sqlite3_step(stmt);
+
+ if (rc != SQLITE_DONE) {
+ sqlite3_finalize(stmt);
+ throw std::runtime_error(std::string("failed to step stmt"));
+ }
+
+ sqlite3_finalize(stmt);
}
-void logout(SQLite::Database &db, const std::string &username) {
- SQLite::Statement stmt(
- db, "UPDATE SESSIONS SET LOGINS=LOGINS-1 WHERE USERNAME=?");
+void record_logout(sqlite3 *db, std::string_view username) {
+ const auto *query = "UPDATE SESSIONS SET LOGINS=LOGINS-1 WHERE USERNAME=?";
+ sqlite3_stmt *stmt;
+ int rc;
+
+ rc = sqlite3_prepare_v2(db, query, -1, &stmt, nullptr);
+
+ if (rc != SQLITE_OK) {
+ throw std::runtime_error(
+ std::string("failed to prepare stmt: {}", sqlite3_errmsg(db)));
+ }
+
+ rc = sqlite3_bind_text(stmt, 1, username.data(), username.length(), nullptr);
+
+ if (rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ throw std::runtime_error(std::string("failed to bind text"));
+ }
+
+ rc = sqlite3_step(stmt);
- stmt.bind(1, username);
+ if (rc != SQLITE_DONE) {
+ sqlite3_finalize(stmt);
+ throw std::runtime_error(std::string("failed to step stmt"));
+ }
- stmt.exec();
+ sqlite3_finalize(stmt);
}
-bool is_logged_in(SQLite::Database &db, const std::string &username) {
- SQLite::Statement stmt(db, "SELECT LOGINS FROM SESSIONS WHERE USERNAME=?");
+bool is_logged_in(sqlite3 *db, std::string_view username) {
+ const auto *query = "SELECT LOGINS FROM SESSIONS WHERE USERNAME=?";
+ sqlite3_stmt *stmt;
+ int rc;
+
+ rc = sqlite3_prepare_v2(db, query, -1, &stmt, nullptr);
+
+ if (rc != SQLITE_OK) {
+ throw std::runtime_error(
+ std::format("failed to prepare stmt: {}", sqlite3_errmsg(db)));
+ }
+
+ rc = sqlite3_bind_text(stmt, 1, username.data(), username.length(), nullptr);
+
+ if (rc != SQLITE_OK) {
+ sqlite3_finalize(stmt);
+ throw std::runtime_error(std::string("failed to bind text"));
+ }
+
+ rc = sqlite3_step(stmt);
- stmt.bind(1, username);
+ if (rc != SQLITE_ROW) {
+ sqlite3_finalize(stmt);
+ throw std::runtime_error(std::string("failed to step stmt"));
+ }
- stmt.executeStep();
+ int logins = sqlite3_column_int(stmt, 0);
- int logins = stmt.getColumn(0);
+ sqlite3_finalize(stmt);
return (logins > 0);
}
+
+void touch(const std::filesystem::path &path, int mode) {
+ auto fd = creat(path.c_str(), mode);
+
+ if (fd == -1) {
+ throw std::runtime_error(std::format("failed to touch file: {} {}",
+ path.c_str(), std::strerror(errno)));
+ }
+
+ close(fd);
+}
+
+void create_directory(const std::filesystem::path &path, std::uint16_t owner,
+ int mode) {
+ int rc;
+
+ rc = mkdir(path.c_str(), mode);
+
+ if (rc == -1) {
+ throw std::runtime_error(std::format("failed to mkdir: {} {}", path.c_str(),
+ std::strerror(errno)));
+ }
+
+ rc = chown(path.c_str(), owner, owner);
+
+ if (rc == -1) {
+ throw std::runtime_error(std::format("failed to chown: {} {}", path.c_str(),
+ std::strerror(errno)));
+ }
+}
} // namespace pam_xdg
-extern "C" int pam_sm_open_session(pam_handle_t *pam, [[gnu::unused]] int flags,
+extern "C" int pam_sm_open_session(pam_handle_t *pamh,
+ [[gnu::unused]] int flags,
[[gnu::unused]] int argc,
[[gnu::unused]] const char **argv) {
const void *data;
- pam_get_item(pam, PAM_USER, &data);
+ if (pam_get_item(pamh, PAM_USER, &data) != PAM_SUCCESS) {
+ std::println(stderr, "failed to get pam item PAM_USER");
+ return PAM_SESSION_ERR;
+ }
auto *username = static_cast<const char *>(data);
if (std::string_view(username) == "root") {
@@ -87,32 +209,49 @@ extern "C" int pam_sm_open_session(pam_handle_t *pam, [[gnu::unused]] int flags,
auto passwd = getpwnam(username);
auto uid = passwd->pw_uid;
- auto user_directory = RUNTIME_DIRECTORY / std::format("{}", uid);
+ auto xdg_runtime_dir =
+ std::filesystem::path(pam_xdg::USER_DIRECTORY) / std::format("{}", uid);
+
+ sqlite3 *db;
try {
- SQLite::Database db(DB_PATH, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
+ if (!std::filesystem::exists(pam_xdg::DB_PATH)) {
+ pam_xdg::touch(pam_xdg::DB_PATH, 0600);
- pam_xdg::create_table(db);
- pam_xdg::create_session(db, username);
+ pam_xdg::open_db(&db, pam_xdg::DB_PATH,
+ SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE);
+
+ pam_xdg::init_db(db);
+ } else {
+ pam_xdg::open_db(&db, pam_xdg::DB_PATH, SQLITE_OPEN_READWRITE);
+ }
+
+ pam_xdg::init_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);
+ if (!std::filesystem::exists(pam_xdg::USER_DIRECTORY)) {
+ pam_xdg::create_directory(pam_xdg::USER_DIRECTORY, 0, 0755);
+ }
+ if (!std::filesystem::is_directory(pam_xdg::USER_DIRECTORY)) {
+ throw std::runtime_error(
+ std::format("{} is not a directory", pam_xdg::USER_DIRECTORY));
}
- pam_xdg::create_directory(user_directory, 0700, uid);
+ pam_xdg::create_directory(std::filesystem::path(pam_xdg::USER_DIRECTORY) /
+ std::format("{}", uid),
+ uid, 0700);
}
- pam_xdg::login(db, username);
+ pam_xdg::record_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"));
+ auto envvar = std::format("XDG_RUNTIME_DIR={}", xdg_runtime_dir.c_str());
+ if (pam_putenv(pamh, envvar.c_str()) != PAM_SUCCESS) {
+ throw std::runtime_error(std::format("failed to putenv: {}", envvar));
}
- } catch (std::exception &e) {
- std::print("{}", e.what());
+
+ } catch (const std::exception &e) {
+ std::println(stderr, "{}", e.what());
+
return PAM_SESSION_ERR;
}
@@ -125,7 +264,10 @@ extern "C" int pam_sm_close_session(pam_handle_t *pam,
[[gnu::unused]] const char **argv) {
const void *data;
- pam_get_item(pam, PAM_USER, &data);
+ if (pam_get_item(pam, PAM_USER, &data) != PAM_SUCCESS) {
+ std::println(stderr, "failed to get pam item PAM_USER");
+ return PAM_SESSION_ERR;
+ }
auto *username = static_cast<const char *>(data);
if (std::string_view(username) == "root") {
@@ -135,20 +277,22 @@ extern "C" int pam_sm_close_session(pam_handle_t *pam,
auto passwd = getpwnam(username);
auto uid = passwd->pw_uid;
- auto user_directory = std::filesystem::path(
- std::filesystem::path(RUNTIME_DIRECTORY) / std::format("{}", uid));
+ auto xdg_runtime_dir =
+ std::filesystem::path(pam_xdg::USER_DIRECTORY) / std::format("{}", uid);
+
+ sqlite3 *db;
try {
- SQLite::Database db(DB_PATH, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
+ pam_xdg::open_db(&db, pam_xdg::DB_PATH, SQLITE_OPEN_READWRITE);
- pam_xdg::logout(db, username);
+ pam_xdg::record_logout(db, username);
if (!pam_xdg::is_logged_in(db, username)) {
- std::filesystem::remove_all(user_directory);
+ std::filesystem::remove_all(xdg_runtime_dir);
}
+ } catch (const std::exception &e) {
+ std::println(stderr, "{}", e.what());
- } catch (std::exception &e) {
- std::print("{}", e.what());
return PAM_SESSION_ERR;
}