Mercurial > crates > nonstick
view src/lib.rs @ 183:4f46681b3f54 default tip
Catch a few stray cargo fmt things.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Wed, 30 Jul 2025 18:43:07 -0400 |
parents | 0730f5f2ee2a |
children |
line wrap: on
line source
//! 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, //! } //! //! // 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. //! } //! } //! //! 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 //! //! 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) //! } //! } //! //! // 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 //! //! - 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 = ""] #![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 { macro_rules! feature_check { ($feature:literal, pam_impl = ($($pimpl:literal),*)) => { #[cfg(all(feature = $feature, not(any($(pam_impl = $pimpl),*))))] compile_error!( concat!( "The feature '", $feature, "' is only available ", "with these PAM implementations:\n", $("- ", $pimpl, "\n"),*, "The current PAM implementation is:\n\n", " ", libpam_sys::pam_impl_name!(), "\n\n", "Set the 'LIBPAMSYS_IMPL' environment variable to one of ", "the above PAM implementation names to build ", "for that implementation of PAM." ) ); } } feature_check!("linux-pam-ext", pam_impl = ("LinuxPam")); feature_check!("basic-ext", pam_impl = ("LinuxPam", "OpenPam")); feature_check!("openpam-ext", pam_impl = ("OpenPam")); feature_check!("sun-ext", pam_impl = ("Sun")); } pub mod constants; pub mod conv; pub mod module; pub mod handle; mod _doc; mod environ; pub mod items; #[cfg(feature = "link")] pub mod libpam; pub mod logging; #[cfg(feature = "link")] #[doc(inline)] pub use crate::libpam::{LibPamHandle, LibPamTransaction, TransactionBuilder}; #[doc(inline)] pub use crate::{ constants::{ AuthnFlags, AuthtokAction, AuthtokFlags, BaseFlags, CredAction, ErrorCode, Result, }, conv::{BinaryData, Conversation, ConversationAdapter}, environ::{EnvironMap, EnvironMapMut}, handle::{ModuleClient, PamShared, Transaction}, module::PamModule, }; use libpam_sys_impls::pam_impl_name;