# HG changeset patch # User Paul Fisher # Date 1751320052 14400 # Node ID 2b255c92417b055c0cddb0bef69db08ccfe10277 # Parent f469b8d9ad7825f974b117f97d8c83a5cb033b28 Introduce base PAM functions; use the real X/SSO PAM header for tests. diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/Cargo.toml --- a/libpam-sys/Cargo.toml Mon Jun 30 04:54:38 2025 -0400 +++ b/libpam-sys/Cargo.toml Mon Jun 30 17:47:32 2025 -0400 @@ -8,6 +8,10 @@ edition.workspace = true rust-version.workspace = true +[features] +default = ["helpers"] +helpers = [] + [dependencies] libpam-sys-impls = { path = "libpam-sys-impls" } diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/README.md --- a/libpam-sys/README.md Mon Jun 30 04:54:38 2025 -0400 +++ b/libpam-sys/README.md Mon Jun 30 17:47:32 2025 -0400 @@ -12,7 +12,16 @@ - Unknown: `XSso` Each implementation exports all the functionality available in its respective PAM library. -`XSso` exports only what is in the [X/SSO specification][xsso]. +`XSso` exports only what is in the [X/SSO specification][xsso]. + +## Features + +The `helpers` feature (optional, but on by default) exports two helpers for PAM memory management. + +- A struct for managing the difference in memory management between Linux-PAM and all other implementations. +- A struct for handling the Linux-PAM–specific binary data payload structure. + +Neither are directly referenced elsewhere, and both allow you to bring your own storage abstractions. ## References diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/libpam-sys-test/Cargo.toml --- a/libpam-sys/libpam-sys-test/Cargo.toml Mon Jun 30 04:54:38 2025 -0400 +++ b/libpam-sys/libpam-sys-test/Cargo.toml Mon Jun 30 17:47:32 2025 -0400 @@ -11,6 +11,5 @@ [build-dependencies] bindgen = "0.72.0" libpam-sys-impls = { path = "../libpam-sys-impls" } -libpam-sys = { path = ".." } quote = "1.0.40" -syn = { version = "2.0.104", default-features = false } +syn = { version = "2.0.104", features = ["full"] } diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/libpam-sys-test/build.rs --- a/libpam-sys/libpam-sys-test/build.rs Mon Jun 30 04:54:38 2025 -0400 +++ b/libpam-sys/libpam-sys-test/build.rs Mon Jun 30 17:47:32 2025 -0400 @@ -1,10 +1,13 @@ use bindgen::MacroTypeVariation; -use libpam_sys::PamImpl; +use libpam_sys_impls::__pam_impl_enum__; use quote::{format_ident, ToTokens}; use std::path::PathBuf; use std::{env, fs}; use syn::{Item, ItemConst, Type, TypePath}; +// We're using the macro directly so we can match exhaustively. +__pam_impl_enum__!(); + fn main() { let config = match PamImpl::CURRENT { PamImpl::LinuxPam => TestConfig { @@ -37,11 +40,10 @@ ..Default::default() }, PamImpl::XSso => TestConfig { - headers: vec!["\"xsso_constants.h\""], + headers: vec!["\"xsso_pam_appl.h\""], ignore_consts: vec!["PAM_CRED_PRELIM_CHECK"], ..Default::default() }, - other => panic!("Unknown PAM implementation {other:?}"), }; generate_const_test(&config); } diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/libpam-sys-test/xsso_constants.h --- a/libpam-sys/libpam-sys-test/xsso_constants.h Mon Jun 30 04:54:38 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -// This list of constants was literally cut-and-pasted from: -// https://pubs.opengroup.org/onlinepubs/8329799/chap5.htm - -#define PAM_SUCCESS 0 -#define PAM_OPEN_ERR 1 -#define PAM_SYMBOL_ERR 2 -#define PAM_SERVICE_ERR 3 -#define PAM_SYSTEM_ERR 4 -#define PAM_BUF_ERR 5 -#define PAM_CONV_ERR 6 -#define PAM_PERM_DENIED 7 -#define PAM_MAXTRIES 8 -#define PAM_AUTH_ERR 9 -#define PAM_NEW_AUTHTOK_REQD 10 -#define PAM_CRED_INSUFFICIENT 11 -#define PAM_AUTHINFO_UNAVAIL 12 -#define PAM_USER_UNKNOWN 13 -#define PAM_CRED_UNAVAIL 14 -#define PAM_CRED_EXPIRED 15 -#define PAM_CRED_ERR 16 -#define PAM_ACCT_EXPIRED 17 -#define PAM_AUTHTOK_EXPIRED 18 -#define PAM_SESSION_ERR 19 -#define PAM_AUTHTOK_ERR 20 -#define PAM_AUTHTOK_RECOVERY_ERR 21 -#define PAM_AUTHTOK_LOCK_BUSY 22 -#define PAM_AUTHTOK_DISABLE_AGING 23 -#define PAM_NO_MODULE_DATA 24 -#define PAM_IGNORE 25 -#define PAM_ABORT 26 -#define PAM_TRY_AGAIN 27 -#define PAM_PROMPT_ECHO_OFF 1 -#define PAM_PROMPT_ECHO_ON 2 -#define PAM_ERROR_MSG 3 -#define PAM_TEXT_INFO 4 -#define PAM_MAX_NUM_MSG 32 -#define PAM_MAX_MSG_SIZE 512 -#define PAM_MAX_RESP_SIZE 512 - -#define PAM_SILENT 0x80000000 -#define PAM_DISALLOW_NULL_AUTHTOK 0x1 -#define PAM_ESTABLISH_CRED 0x1 -#define PAM_DELETE_CRED 0x2 -#define PAM_REINITIALISE_CRED 0x4 -#define PAM_REFRESH_CRED 0x8 -#define PAM_CRED_PRELIM_CHECK 0x1 -#define PAM_UPDATE_AUTHTOK 0x2 -#define PAM_CHANGE_EXPIRED_AUTHTOK 0x4 - -#define PAM_SERVICE 1 -#define PAM_USER 2 -#define PAM_TTY 3 -#define PAM_RHOST 4 -#define PAM_CONV 5 -#define PAM_AUTHTOK 6 -#define PAM_OLDAUTHTOK 7 -#define PAM_RUSER 8 -#define PAM_USER_PROMPT 9 - -// Some of the names in here aren't used by anybody, though. -// These aliases are, though. -#define PAM_REINITIALIZE_CRED PAM_REINITIALISE_CRED -#define PAM_PRELIM_CHECK PAM_CRED_PRELIM_CHECK diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/libpam-sys-test/xsso_pam_appl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpam-sys/libpam-sys-test/xsso_pam_appl.h Mon Jun 30 17:47:32 2025 -0400 @@ -0,0 +1,139 @@ +/* + * The contents of this header are copied directly from the X/SSO PAM spec, + * with comments and unpopular functions removed. + * + * https://pubs.opengroup.org/onlinepubs/8329799/apdxa.htm + */ +#define PAM_SUCCESS 0 +#define PAM_OPEN_ERR 1 +#define PAM_SYMBOL_ERR 2 +#define PAM_SERVICE_ERR 3 +#define PAM_SYSTEM_ERR 4 +#define PAM_BUF_ERR 5 +#define PAM_CONV_ERR 6 +#define PAM_PERM_DENIED 7 +#define PAM_MAXTRIES 8 +#define PAM_AUTH_ERR 9 +#define PAM_NEW_AUTHTOK_REQD 10 +#define PAM_CRED_INSUFFICIENT 11 +#define PAM_AUTHINFO_UNAVAIL 12 +#define PAM_USER_UNKNOWN 13 +#define PAM_CRED_UNAVAIL 14 +#define PAM_CRED_EXPIRED 15 +#define PAM_CRED_ERR 16 +#define PAM_ACCT_EXPIRED 17 +#define PAM_AUTHTOK_EXPIRED 18 +#define PAM_SESSION_ERR 19 +#define PAM_AUTHTOK_ERR 20 +#define PAM_AUTHTOK_RECOVERY_ERR 21 +#define PAM_AUTHTOK_LOCK_BUSY 22 +#define PAM_AUTHTOK_DISABLE_AGING 23 +#define PAM_NO_MODULE_DATA 24 +#define PAM_IGNORE 25 +#define PAM_ABORT 26 +#define PAM_TRY_AGAIN 27 +/* PAM_MODULE_UNKNOWN and PAM_DOMAIN_UNKNOWN are not universal. */ + +struct pam_message { + int msg_style; + char *msg; +}; + +#define PAM_PROMPT_ECHO_OFF 1 +#define PAM_PROMPT_ECHO_ON 2 +#define PAM_ERROR_MSG 3 +#define PAM_TEXT_INFO 4 + +#define PAM_MAX_NUM_MSG 32 +#define PAM_MAX_MSG_SIZE 512 +#define PAM_MAX_RESP_SIZE 512 + +struct pam_response { + char *resp; + int resp_retcode; +}; + +struct pam_conv { + int (*conv)(int, struct pam_message **, struct pam_response **, void *); + void *appdata_ptr; +}; + +typedef struct pam_handle pam_handle_t; + +extern int pam_start(const char *service_name, + const char *user, + const struct pam_conv *pam_conv, + pam_handle_t **pamh); + +extern int pam_end(pam_handle_t *pamh, int status); + +extern int pam_set_item(pam_handle_t *pamh, int item_type, const void *item); + +extern int pam_get_item(const pam_handle_t *pamh, int item_type, void **item); + +#define PAM_SERVICE 1 +#define PAM_USER 2 +#define PAM_TTY 3 +#define PAM_RHOST 4 +#define PAM_CONV 5 +#define PAM_AUTHTOK 6 +#define PAM_OLDAUTHTOK 7 +#define PAM_RUSER 8 +#define PAM_USER_PROMPT 9 + +extern int pam_get_user(pam_handle_t *pamh, char **user, const char *prompt); + +extern int pam_set_data(pam_handle_t *pamh, + const char *module_data_name, + const void *data, + void (*cleanup)(pam_handle_t *pamh, + void *data, + int pam_end_status)); + +extern int pam_get_data(const pam_handle_t *pamh, + const char *module_data_name, + void **data); + +extern char *pam_strerror(pam_handle_t *pamh, int errnum); + +#define PAM_SILENT 0x80000000 + +extern int pam_authenticate(pam_handle_t *pamh, int flags); + +#define PAM_DISALLOW_NULL_AUTHTOK 0x1 + +/* Nobody implements pam_authenticate_secondary. */ + +extern int pam_acct_mgmt(pam_handle_t *pamh, int flags); + +extern int pam_open_session(pam_handle_t *pamh, int flags); + +extern int pam_close_session(pam_handle_t *pamh, int flags); + +extern int pam_setcred(pam_handle_t *pamh, int flags); + +#define PAM_ESTABLISH_CRED 0x1 +#define PAM_DELETE_CRED 0x2 +#define PAM_REINITIALIZE_CRED 0x4 + +#define PAM_REFRESH_CRED 0x8 + +extern int pam_chauthtok(pam_handle_t *pamh, int flags); + +#define PAM_CHANGE_EXPIRED_AUTHTOK 0x4 + +extern char *pam_getenv(pam_handle_t *pamh, const char *name); + +extern char **pam_getenvlist(pam_handle_t *pamh); + +extern int pam_putenv(pam_handle_t *pamh, const char *namevalue); + +/* Nobody implements the _mapped functions. */ + +extern int pam_get_user(pam_handle_t *pamh, char **user, const char *prompt); + +/* The following constants come from the `pam_module.h` part of the page. */ +#define PAM_PRELIM_CHECK 0x1 +#define PAM_UPDATE_AUTHTOK 0x2 + +/* The _sm functions are not exported symbols, but prototypes for modules. */ diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/src/constants.rs --- a/libpam-sys/src/constants.rs Mon Jun 30 04:54:38 2025 -0400 +++ b/libpam-sys/src/constants.rs Mon Jun 30 17:47:32 2025 -0400 @@ -194,8 +194,6 @@ PAM_REINITIALIZE_CRED = 0b0100; PAM_REFRESH_CRED = 0b1000; ); - #[deprecated = "everybody spells it with a Z nowadays"] - pub const PAM_REINITIALISE_CRED: i32 = 0b0100; define!( /// A flag for `pam_sm_chauthtok`. @@ -203,8 +201,6 @@ PAM_UPDATE_AUTHTOK = 0b0010; PAM_CHANGE_EXPIRED_AUTHTOK = 0b0100; ); - #[deprecated = "modern PAM implementations use PAM_PRELIM_CHECK"] - pub const PAM_CRED_PRELIM_CHECK: i32 = 0b0001; } #[cfg_pam_impl("OpenPam")] @@ -272,4 +268,3 @@ /// A flag for `pam_chauthtok`. pub const PAM_NO_AUTHTOK_CHECK: i32 = 0b1000; } - diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/src/funcs/xsso_base.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpam-sys/src/funcs/xsso_base.rs Mon Jun 30 17:47:32 2025 -0400 @@ -0,0 +1,97 @@ +//! Only the very base functions described in the X/SSO specification. + +use crate::pam_conv; +use crate::structs::{pam_handle_t, CleanupCallback}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + /// Account validation. + pub fn pam_acct_mgmt(pamh: *mut pam_handle_t, flags: c_int) -> c_int; + + /// Authenticate a user. + pub fn pam_authenticate(pamh: *mut pam_handle_t, flags: c_int) -> c_int; + + // Nobody implements pam_authenticate_secondary. + + /// Manage authentication tokens. + pub fn pam_chauthtok(pamh: *mut pam_handle_t, flags: c_int) -> c_int; + + /// Close an opened user session. + pub fn pam_close_session(pamh: *mut pam_handle_t, flags: c_int) -> c_int; + + /// Ends the PAM transaction. + pub fn pam_end(pamh: *mut pam_handle_t, flags: c_int) -> c_int; + + /// Gets module-specific data. PAM still owns the data. + pub fn pam_get_data( + pamh: *mut pam_handle_t, + module_data_name: *const c_char, + data: &mut *const c_void, + ) -> c_int; + + /// Gets an environment variable. You own the return value. + pub fn pam_getenv(pamh: *mut pam_handle_t, name: *const c_char) -> *mut c_char; + + /// Gets all the environment variables. You own everything it points to. + pub fn pam_getenvlist(pamh: *mut pam_handle_t) -> *mut *mut c_char; + + /// Get information about the transaction. + pub fn pam_get_item( + pamh: *mut pam_handle_t, + item_type: c_int, + item: &mut *const c_void, + ) -> c_int; + + // Nobody implements pam_get_mapped_authtok. + // Nobody implements pam_get_mapped_username. + + /// Get the username. + pub fn pam_get_user( + pamh: *mut pam_handle_t, + user: &mut *const c_char, + prompt: *const c_char, + ) -> c_int; + + /// Opens a user session. + pub fn pam_open_session(pamh: *mut pam_handle_t, flags: c_int) -> c_int; + + /// Sets the value of an environment variable. `namevalue` is copied. + pub fn pam_putenv(pamh: *mut pam_handle_t, namevalue: *const c_char) -> c_int; + + /// Update or delete user credentials. + pub fn pam_setcred(pamh: *mut pam_handle_t, flags: c_int) -> c_int; + + /// Set module-specific data. + pub fn pam_set_data( + pamh: *mut pam_handle_t, + module_data_name: *const c_char, + data: *mut c_void, + cleanup: CleanupCallback, + ) -> c_int; + + /// Set information about the transaction. The `item` is copied. + pub fn pam_set_item(pamh: *mut pam_handle_t, item_type: c_int, item: *const c_void) -> c_int; + + // Nobody implements pam_set_mapped_authtok. + // Nobody implements pam_set_mapped_username. + + // The pam_sm_whatever functions are prototypes for the functions that + // a PAM module should implement, not symbols provided by PAM. + + // Nobody implements pam_authenticate_secondary. + + /// Starts a PAM transaction. The `conv` may or may not be copied. + pub fn pam_start( + service: *const c_char, + user: *const c_char, + pam_conv: *mut pam_conv, + pamh: &mut *mut pam_handle_t, + ); + + /// Gets a statically-allocated error string. + /// + /// All implementations of PAM known to this library (Linux-PAM, OpenPAM, + /// and Sun) ignore `pamh` and will accept a null pointer. + pub fn pam_strerror(pamh: *const pam_handle_t, error_number: c_int) -> *const c_char; + +} diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/src/functions.rs --- a/libpam-sys/src/functions.rs Mon Jun 30 04:54:38 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ - diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/src/helpers.rs --- a/libpam-sys/src/helpers.rs Mon Jun 30 04:54:38 2025 -0400 +++ b/libpam-sys/src/helpers.rs Mon Jun 30 17:47:32 2025 -0400 @@ -57,13 +57,21 @@ /// ``` /// /// [conversation callback]: crate::ConversationCallback -/// [message]: crate::Message +/// [message]: crate::pam_message #[derive(Debug)] pub struct PtrPtrVec { data: Vec, pointers: Vec<*const T>, } +// Since this is a wrapper around a Vec with no dangerous functionality*, +// this can be Send and Sync provided the original Vec is. +// +// * It will only become unsafe when the user dereferences a pointer or sends it +// to an unsafe function. +unsafe impl Send for PtrPtrVec where Vec: Send {} +unsafe impl Sync for PtrPtrVec where Vec: Sync {} + impl PtrPtrVec { /// Takes ownership of the given Vec and creates a vec of pointers to it. pub fn new(data: Vec) -> Self { @@ -509,14 +517,16 @@ fn test_iter_ptr_ptr() { let strs = vec![Box::new("a"), Box::new("b"), Box::new("c"), Box::new("D")]; let ptr: *const *const &str = strs.as_ptr().cast(); - let got: Vec<&str> = unsafe { - PtrPtrVec::iter_over_linux(ptr, 4) - }.cloned().collect(); + let got: Vec<&str> = unsafe { PtrPtrVec::iter_over_linux(ptr, 4) } + .cloned() + .collect(); assert_eq!(vec!["a", "b", "c", "D"], got); let nums = [-1i8, 2, 3]; let ptr = nums.as_ptr(); - let got: Vec = unsafe { PtrPtrVec::iter_over_xsso(&ptr, 3)}.cloned().collect(); + let got: Vec = unsafe { PtrPtrVec::iter_over_xsso(&ptr, 3) } + .cloned() + .collect(); assert_eq!(vec![255, 2, 3], got); } } diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/src/lib.rs --- a/libpam-sys/src/lib.rs Mon Jun 30 04:54:38 2025 -0400 +++ b/libpam-sys/src/lib.rs Mon Jun 30 17:47:32 2025 -0400 @@ -6,11 +6,6 @@ use libpam_sys_impls::{__pam_impl_enum__, __pam_impl_name__}; -mod constants; -mod functions; -pub mod helpers; -mod structs; - /// A `cfg`-like attribute macro for code specific to one PAM implementation. /// /// Different versions of PAM export different functions and have some @@ -48,9 +43,17 @@ #[doc(inline)] pub use libpam_sys_impls::cfg_pam_impl; +mod constants; +// We get `funcs` from different places depending upon the PAM implementation. +// This is because +#[path = "funcs/xsso_base.rs"] +mod funcs; +pub mod helpers; +mod structs; + +#[doc(inline)] +pub use crate::{constants::*, funcs::*, structs::*}; + // Looking for the actual code defining this enum? // It's in the build.rs file for libpam_sys_impls. __pam_impl_enum__!(#[non_exhaustive]); - -#[doc(inline)] -pub use crate::{constants::*, structs::*}; diff -r f469b8d9ad78 -r 2b255c92417b libpam-sys/src/structs.rs --- a/libpam-sys/src/structs.rs Mon Jun 30 04:54:38 2025 -0400 +++ b/libpam-sys/src/structs.rs Mon Jun 30 17:47:32 2025 -0400 @@ -1,11 +1,17 @@ +//! Structs and wrappers that PAM is made of. +#![allow(non_camel_case_types)] + use std::ffi::{c_int, c_void}; use std::fmt; use std::marker::{PhantomData, PhantomPinned}; /// A marker struct to make whatever it's in `!Sync`, `!Send`, and `!Unpin`. #[derive(Default, PartialOrd, PartialEq, Ord, Eq)] -#[repr(transparent)] -struct ExtremelyUnsafe(PhantomData<(PhantomPinned, *mut c_void)>); +#[repr(C)] +struct ExtremelyUnsafe { + _value: (), + _marker: PhantomData<(PhantomPinned, *mut c_void)>, +} impl fmt::Debug for ExtremelyUnsafe { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -17,20 +23,17 @@ /// /// This is only ever returned in pointer form and cannot be constructed. #[repr(C)] -pub struct PamHandle { - _marker: ExtremelyUnsafe, -} +pub struct pam_handle_t(ExtremelyUnsafe); -impl fmt::Debug for PamHandle { +impl fmt::Debug for pam_handle_t { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "PamHandle({self:p}") } } /// An opaque structure that is passed through PAM in a conversation. -pub struct AppData { - _marker: ExtremelyUnsafe, -} +#[repr(C)] +pub struct AppData(ExtremelyUnsafe); impl fmt::Debug for AppData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -47,30 +50,38 @@ // This is a *const *const because accessing memory from a reference // outside its bounds is undefined behavior, and *messages is an array // in X/SSO PAM impls. - messages: *const *const Message, - // This is a &mut *mut because the caller sets the pointer in `responses` + msg: *const *const pam_message, + // This is a &mut *mut because the caller sets the pointer in `resp` // but does not mess around outside its memory space. - responses: &mut *mut Response, + resp: &mut *mut pam_response, appdata: *const AppData, ) -> c_int; +/// Called to clean up data set using [`pam_set_data`](crate::pam_set_data). +pub type CleanupCallback = unsafe extern "C" fn( + pamh: *mut pam_handle_t, + data: *mut c_void, + pam_end_status: c_int, +) -> c_int; + /// Used by PAM to communicate between the module and the application. #[repr(C)] -pub struct Conversation { - pub callback: ConversationCallback, - pub appdata: *const AppData, +pub struct pam_conv { + pub conv: ConversationCallback, + pub appdata_ptr: *const AppData, } /// A message sent into a PAM conversation. #[repr(C)] -pub struct Message { - pub style: c_int, - pub data: *const c_void, +pub struct pam_message { + pub msg_style: c_int, + pub msg: *const c_void, } /// A response returned from a PAM conversation. #[repr(C)] -pub struct Response { - pub data: *mut c_void, - pub _unused: c_int, +pub struct pam_response { + pub resp: *mut c_void, + /// Completely unused. + pub resp_retcode: c_int, }