3.8 KiB
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 sourcerust-backend/: Rust static library backendtests/: Integration tests
Build Requirements
- GNU g++ (C++17)
- CMake >= 3.15
- Rust (cargo)
- PAM development headers
Build Instructions
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— Cprintf-style helpers.pam-module/include/rust_backend_logging_cpp.h— C++cout/cerr-style RAII stream helpers.
C example (printf-style):
#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;
vsnprintferrors or a NULL format will log a small fallback message.
C++ example (stream-style):
#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()andRUST_CERR()are thin factories that return aRustLogStreamobject which buffers viastd::ostringstreamand sends the accumulated string torust_log_event_with_levelwhen the temporary is destroyed.- The destructor is
noexceptand 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 toINFOfor 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
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:
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:
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:
auth required pam_module.so debug log_path=/var/log/pam_rust_backend.log
account required pam_permit.so
Run the test client
./tests/pam_test_app pam_test_app <user> <password>
The first argument (pam_test_app) must match the service filename in /etc/pam.d/pam_test_app.