Mercurial > crates > nonstick
diff src/lib.rs @ 174:9e4ce1631bd3
Dramatically expand documentation.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 29 Jul 2025 18:58:27 -0400 |
parents | 46e8ce5cd5d1 |
children | e30775c80b49 |
line wrap: on
line diff
--- a/src/lib.rs Tue Jul 29 16:52:32 2025 -0400 +++ b/src/lib.rs Tue Jul 29 18:58:27 2025 -0400 @@ -1,26 +1,171 @@ -//! A safe, nonstick interface to PAM. +//! A safe, nonstick interface to the Pluggable Authentication Module framework. +//! +//! Nonstick provides a fully type- and memory-safe interface to +//! all implementations of PAM, both for PAM modules and PAM applications. +//! +//! # Usage +//! +//! nonstick can be used on either side of a PAM transaction, +//! both to implement an application which calls into PAM, +//! or a module which implements a PAM backend. +//! +//! For more information about how PAM works in general, or more pointers +//! on how to implement a PAM module or application, see the +//! [References](#references) section. +//! +//! ## PAM Application +//! +//! To implement a PAM application, first implement a [`Conversation`], +//! then build a [`Transaction`] with the [`TransactionBuilder`]. +//! This can be built into any standard Rust library or binary. +//! +//! ``` +//! use nonstick::{ +//! AuthnFlags, Conversation, ConversationAdapter, Result as PamResult, Transaction, +//! TransactionBuilder, +//! }; +//! use std::ffi::{OsStr, OsString}; +//! +//! /// A basic Conversation that assumes that any "regular" prompt is for +//! /// the username, and that any "masked" prompt is for the password. +//! /// +//! /// A typical Conversation will provide the user with an interface +//! /// to interact with PAM, e.g. a dialogue box or a terminal prompt. +//! struct UsernamePassConvo { +//! username: String, +//! password: String, +//! } //! -//! This implements a type-safe library to interact with PAM. -//! Currently, it implements a subset of PAM useful for implementing a module. +//! // ConversationAdapter is a convenience wrapper for the common case +//! // of only handling one request at a time. +//! impl ConversationAdapter for UsernamePassConvo { +//! fn prompt(&self, request: impl AsRef<OsStr>) -> PamResult<OsString> { +//! Ok(OsString::from(&self.username)) +//! } +//! +//! fn masked_prompt(&self, request: impl AsRef<OsStr>) -> PamResult<OsString> { +//! Ok(OsString::from(&self.password)) +//! } +//! +//! fn error_msg(&self, message: impl AsRef<OsStr>) { +//! // Normally you would want to display this to the user somehow. +//! // In this case, we're just ignoring it. +//! } +//! +//! fn info_msg(&self, message: impl AsRef<OsStr>) { +//! // ibid. +//! } +//! } //! -//! To write a new PAM module using this crate: +//! fn authenticate(username: &str, password: &str) -> PamResult<()> { +//! let user_pass = UsernamePassConvo { +//! username: username.into(), +//! password: password.into(), +//! }; +//! +//! let mut txn = TransactionBuilder::new_with_service("cortex-sso") +//! .username(username) +//! .build(user_pass.into_conversation())?; +//! // If authentication fails, this will return an error. +//! // We immediately give up rather than re-prompting the user. +//! txn.authenticate(AuthnFlags::empty())?; +//! txn.account_management(AuthnFlags::empty())?; +//! Ok(()) +//! } +//! ``` +//! +//! PAM just tells you that the user is, in fact, who they say they are. +//! It is up to your application to choose what to do with that information. +//! +//! ## PAM module //! -//! 1. Create a `dylib` crate. -//! 2. Implement a subset of the functions in the [`PamModule`] trait -//! corresponding to what you want your module to do. -//! In the simplest case (for a new password-based authenticator), -//! this will be the [`PamModule::authenticate`] function. -//! 3. Export your PAM module using the [`pam_export!`] macro. -//! 4. Build and install the dynamic library. -//! This usually entails placing it at -//! <code>/usr/lib/security/pam_<var>your_module</var>.so</code>, -//! or maybe -//! <code>/usr/lib/<var>your-architecture</var>/security/pam_<var>your_module</var>.so</code>. +//! PAM modules are implemented as dynamic libraries loaded into +//! the address space of the calling application. To implement a module, +//! create a `dylib` crate and implement a [`PamModule`], and export it +//! using the [`pam_export!`] macro. +//! +//! ```toml +//! ## Your Cargo.toml +//! [package] +//! name = "example-package" +//! ## ... +//! +//! [lib] +//! crate-type = ["cdylib"] +//! ``` +//! +//! ``` +//! // Your lib.rs +//! +//! use nonstick::{ +//! pam_export, AuthnFlags, ErrorCode, ModuleClient, PamModule, Result as PamResult, +//! }; +//! use std::ffi::CStr; +//! +//! # // This needs to be here to make this doc example work. +//! # fn main() {} +//! +//! /// A module that only allows you to log in if your username +//! /// is the same as your password. +//! struct SameName; +//! pam_export!(SameName); +//! +//! impl<M: ModuleClient> PamModule<M> for SameName { +//! fn authenticate(handle: &mut M, _args: Vec<&CStr>, _flags: AuthnFlags) -> PamResult<()> { +//! // Using `None` as the prompt parameter here will tell PAM +//! // to use the default prompt. +//! let username = handle.username(None)?; +//! let password = handle.authtok(None)?; +//! if username == password { +//! Ok(()) +//! } else { +//! Err(ErrorCode::AuthenticationError) +//! } +//! } //! -//! For general information on writing PAM modules, see -//! [The Linux-PAM Module Writers' Guide][module-guide] +//! // You can implement other methods of PamModule to provide additional +//! // features. +//! } +//! ``` +//! +//! This gets built into a library like `pam_samename.so`. By installing this +//! into your PAM library directory and configuring PAM to use it in +//! the authentication stack (beyond the scope of this documentation), it will +//! be used to authenticate users. +//! +//! # Configuration +//! +//! There are a few different PAM implementations available. By default, +//! nonstick detects which implementation it should use for the current target. +//! If you need to choose a different implementation, set the `LIBPAMSYS_IMPL` +//! environment variable at build time. See the [`libpam_sys`] documentation. +#![doc = concat!("This documentation was built for **", pam_impl_name!(), "**.")] +//! +//! # References //! -//! [module-guide]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/Linux-PAM_MWG.html +//! - The Linux-PAM guides provide information for a variety of audiences. +//! While some of it is specific to Linux-PAM, much of it applies to other +//! PAM implementations: +//! - [Application Developers' Guide][adg] +//! - [Module Writers' Guide][mwg] +//! - [System Administrators' Guide][sag] +//! - PAM framework man page for developers: +//! - [Linux-PAM developer man page][man7] +//! - [OpenPAM developer man page][manbsd] +//! - [Illumos PAM developer man page][mansun] +//! - PAM framework man page for system administrators: +//! - [Linux-PAM admin documentation][man7pam8] +//! - [OpenPAM admin documentation][bsdpam8] +//! - [Illumos pam.conf documentation][sunpam5] +//! - [The original PAM specification][spec] (mostly of historical interest) +#![doc = crate::_doc::man7!(man7pam8: 8 pam)] +#![doc = crate::_doc::manbsd!(bsdpam8: 8 pam)] +#![doc = crate::_doc::mansun!(sunpam5: 5 "pam.conf")] +#![doc = crate::_doc::stdlinks!(3 pam)] +#![doc = crate::_doc::guide!(adg: "Linux-PAM_ADG.html")] +#![doc = crate::_doc::guide!(mwg: "Linux-PAM_MWG.html")] +#![doc = crate::_doc::guide!(sag: "Linux-PAM_SAG.html")] +#![doc = crate::_doc::xsso!(spec: "toc.htm")] #[cfg(feature = "link")] mod _compat_checker { @@ -73,3 +218,4 @@ handle::{ModuleClient, PamShared, Transaction}, module::PamModule, }; +use libpam_sys::pam_impl_name;