# PAM C++ + Rust Backend Monorepo This project provides a Linux PAM module written in C++ (GNU g++, C++17) that delegates authentication and logging to a Rust static library backend. The build is orchestrated by CMake, which triggers Cargo for the Rust backend. ## Structure - `pam-module/`: C++ PAM module source - `rust-backend/`: Rust static library backend - `tests/`: Integration tests ## Build Requirements - GNU g++ (C++17) - CMake >= 3.15 - Rust (cargo) - PAM development headers ## Build Instructions ```bash mkdir build && cd build cmake .. cmake --build . ``` ## Install Copy the built PAM module to `/lib/security/` or `/lib64/security/` as needed. ## Logging Rust backend logs to `/var/log/pam_rust_backend.log` by default. ## C / C++ Logging Wrappers (examples) This project exposes a simple Rust FFI sink and provides lightweight C and C++ wrappers for convenient logging without changing the Rust side. Headers: - `pam-module/include/rust_backend_ffi.h` — canonical FFI declarations and level enum. - `pam-module/include/rust_backend_logging_c.h` — C `printf`-style helpers. - `pam-module/include/rust_backend_logging_cpp.h` — C++ `cout`/`cerr`-style RAII stream helpers. C example (printf-style): ```c #include "rust_backend_logging_c.h" // simple formatted info log RUST_LOGF_INFO("user=%s result=%d", user, result); ``` C notes: - Formatting uses a fixed buffer (`RUST_LOG_FMT_BUFSZ`, default 2048) and is performed in C; the formatted string is forwarded to Rust. - If the formatted output is longer than the buffer it will be truncated; `vsnprintf` errors or a NULL format will log a small fallback message. C++ example (stream-style): ```cpp #include "rust_backend_ffi.h" // stream-style; message sent to Rust on destructor RUST_COUT() << "user=" << user << " authenticated=" << (ok?"yes":"no") << std::flush; // error example RUST_CERR() << "authentication failed for user=" << user << std::flush; ``` C++ notes: - `RUST_COUT()` and `RUST_CERR()` are thin factories that return a `RustLogStream` object which buffers via `std::ostringstream` and sends the accumulated string to `rust_log_event_with_level` when the temporary is destroyed. - The destructor is `noexcept` and swallow exceptions to avoid crossing FFI boundaries. Compatibility: - The canonical ABI function is `rust_log_event_with_level(const char* event, RustLogLevel level)` and is implemented in Rust. - A legacy `rust_log_event(const char* event)` wrapper remains and maps to `INFO` for backward compatibility. - Wrappers are header-only and additive; no changes to the Rust backend are required. ## Safety - Rust panics are contained and never cross FFI. - C++ exceptions are caught before returning to PAM. ## Extending Add new subprojects as needed for future business logic or integrations. ## Test Application (PAM Client) This repository includes a test PAM client at `tests/pam_test_app.cpp`. ### Build the test ```bash mkdir -p build && cd build cmake .. cmake --build . ``` The executable will be generated at `build/tests/pam_test_app`. ### Copy the PAM module After building, copy the PAM module to the system PAM module path: ```bash sudo cp build/pam-module/pam_module.so /lib/security/ ``` On some distributions, use `/lib64/security/` instead. ### Configure `/etc/pam.d` service Create `/etc/pam.d/pam_test_app` with: ```conf auth required pam_module.so account required pam_permit.so ``` You can also pass module arguments which are exposed as `argc` and `argv` to `pam_sm_authenticate`, for example: ```conf auth required pam_module.so debug log_path=/var/log/pam_rust_backend.log account required pam_permit.so ``` ### Run the test client ```bash ./tests/pam_test_app pam_test_app ``` The first argument (`pam_test_app`) must match the service filename in `/etc/pam.d/pam_test_app`.