Mercurial > crates > nonstick
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 173:46e8ce5cd5d1 | 174:9e4ce1631bd3 |
|---|---|
| 1 //! A safe, nonstick interface to PAM. | 1 //! A safe, nonstick interface to the Pluggable Authentication Module framework. |
| 2 //! | 2 //! |
| 3 //! This implements a type-safe library to interact with PAM. | 3 //! Nonstick provides a fully type- and memory-safe interface to |
| 4 //! Currently, it implements a subset of PAM useful for implementing a module. | 4 //! all implementations of PAM, both for PAM modules and PAM applications. |
| 5 //! | 5 //! |
| 6 //! To write a new PAM module using this crate: | 6 //! # Usage |
| 7 //! | 7 //! |
| 8 //! 1. Create a `dylib` crate. | 8 //! nonstick can be used on either side of a PAM transaction, |
| 9 //! 2. Implement a subset of the functions in the [`PamModule`] trait | 9 //! both to implement an application which calls into PAM, |
| 10 //! corresponding to what you want your module to do. | 10 //! or a module which implements a PAM backend. |
| 11 //! In the simplest case (for a new password-based authenticator), | 11 //! |
| 12 //! this will be the [`PamModule::authenticate`] function. | 12 //! For more information about how PAM works in general, or more pointers |
| 13 //! 3. Export your PAM module using the [`pam_export!`] macro. | 13 //! on how to implement a PAM module or application, see the |
| 14 //! 4. Build and install the dynamic library. | 14 //! [References](#references) section. |
| 15 //! This usually entails placing it at | 15 //! |
| 16 //! <code>/usr/lib/security/pam_<var>your_module</var>.so</code>, | 16 //! ## PAM Application |
| 17 //! or maybe | 17 //! |
| 18 //! <code>/usr/lib/<var>your-architecture</var>/security/pam_<var>your_module</var>.so</code>. | 18 //! To implement a PAM application, first implement a [`Conversation`], |
| 19 //! | 19 //! then build a [`Transaction`] with the [`TransactionBuilder`]. |
| 20 //! For general information on writing PAM modules, see | 20 //! This can be built into any standard Rust library or binary. |
| 21 //! [The Linux-PAM Module Writers' Guide][module-guide] | 21 //! |
| 22 //! | 22 //! ``` |
| 23 //! [module-guide]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/Linux-PAM_MWG.html | 23 //! use nonstick::{ |
| 24 //! AuthnFlags, Conversation, ConversationAdapter, Result as PamResult, Transaction, | |
| 25 //! TransactionBuilder, | |
| 26 //! }; | |
| 27 //! use std::ffi::{OsStr, OsString}; | |
| 28 //! | |
| 29 //! /// A basic Conversation that assumes that any "regular" prompt is for | |
| 30 //! /// the username, and that any "masked" prompt is for the password. | |
| 31 //! /// | |
| 32 //! /// A typical Conversation will provide the user with an interface | |
| 33 //! /// to interact with PAM, e.g. a dialogue box or a terminal prompt. | |
| 34 //! struct UsernamePassConvo { | |
| 35 //! username: String, | |
| 36 //! password: String, | |
| 37 //! } | |
| 38 //! | |
| 39 //! // ConversationAdapter is a convenience wrapper for the common case | |
| 40 //! // of only handling one request at a time. | |
| 41 //! impl ConversationAdapter for UsernamePassConvo { | |
| 42 //! fn prompt(&self, request: impl AsRef<OsStr>) -> PamResult<OsString> { | |
| 43 //! Ok(OsString::from(&self.username)) | |
| 44 //! } | |
| 45 //! | |
| 46 //! fn masked_prompt(&self, request: impl AsRef<OsStr>) -> PamResult<OsString> { | |
| 47 //! Ok(OsString::from(&self.password)) | |
| 48 //! } | |
| 49 //! | |
| 50 //! fn error_msg(&self, message: impl AsRef<OsStr>) { | |
| 51 //! // Normally you would want to display this to the user somehow. | |
| 52 //! // In this case, we're just ignoring it. | |
| 53 //! } | |
| 54 //! | |
| 55 //! fn info_msg(&self, message: impl AsRef<OsStr>) { | |
| 56 //! // ibid. | |
| 57 //! } | |
| 58 //! } | |
| 59 //! | |
| 60 //! fn authenticate(username: &str, password: &str) -> PamResult<()> { | |
| 61 //! let user_pass = UsernamePassConvo { | |
| 62 //! username: username.into(), | |
| 63 //! password: password.into(), | |
| 64 //! }; | |
| 65 //! | |
| 66 //! let mut txn = TransactionBuilder::new_with_service("cortex-sso") | |
| 67 //! .username(username) | |
| 68 //! .build(user_pass.into_conversation())?; | |
| 69 //! // If authentication fails, this will return an error. | |
| 70 //! // We immediately give up rather than re-prompting the user. | |
| 71 //! txn.authenticate(AuthnFlags::empty())?; | |
| 72 //! txn.account_management(AuthnFlags::empty())?; | |
| 73 //! Ok(()) | |
| 74 //! } | |
| 75 //! ``` | |
| 76 //! | |
| 77 //! PAM just tells you that the user is, in fact, who they say they are. | |
| 78 //! It is up to your application to choose what to do with that information. | |
| 79 //! | |
| 80 //! ## PAM module | |
| 81 //! | |
| 82 //! PAM modules are implemented as dynamic libraries loaded into | |
| 83 //! the address space of the calling application. To implement a module, | |
| 84 //! create a `dylib` crate and implement a [`PamModule`], and export it | |
| 85 //! using the [`pam_export!`] macro. | |
| 86 //! | |
| 87 //! ```toml | |
| 88 //! ## Your Cargo.toml | |
| 89 //! [package] | |
| 90 //! name = "example-package" | |
| 91 //! ## ... | |
| 92 //! | |
| 93 //! [lib] | |
| 94 //! crate-type = ["cdylib"] | |
| 95 //! ``` | |
| 96 //! | |
| 97 //! ``` | |
| 98 //! // Your lib.rs | |
| 99 //! | |
| 100 //! use nonstick::{ | |
| 101 //! pam_export, AuthnFlags, ErrorCode, ModuleClient, PamModule, Result as PamResult, | |
| 102 //! }; | |
| 103 //! use std::ffi::CStr; | |
| 104 //! | |
| 105 //! # // This needs to be here to make this doc example work. | |
| 106 //! # fn main() {} | |
| 107 //! | |
| 108 //! /// A module that only allows you to log in if your username | |
| 109 //! /// is the same as your password. | |
| 110 //! struct SameName; | |
| 111 //! pam_export!(SameName); | |
| 112 //! | |
| 113 //! impl<M: ModuleClient> PamModule<M> for SameName { | |
| 114 //! fn authenticate(handle: &mut M, _args: Vec<&CStr>, _flags: AuthnFlags) -> PamResult<()> { | |
| 115 //! // Using `None` as the prompt parameter here will tell PAM | |
| 116 //! // to use the default prompt. | |
| 117 //! let username = handle.username(None)?; | |
| 118 //! let password = handle.authtok(None)?; | |
| 119 //! if username == password { | |
| 120 //! Ok(()) | |
| 121 //! } else { | |
| 122 //! Err(ErrorCode::AuthenticationError) | |
| 123 //! } | |
| 124 //! } | |
| 125 //! | |
| 126 //! // You can implement other methods of PamModule to provide additional | |
| 127 //! // features. | |
| 128 //! } | |
| 129 //! ``` | |
| 130 //! | |
| 131 //! This gets built into a library like `pam_samename.so`. By installing this | |
| 132 //! into your PAM library directory and configuring PAM to use it in | |
| 133 //! the authentication stack (beyond the scope of this documentation), it will | |
| 134 //! be used to authenticate users. | |
| 135 //! | |
| 136 //! # Configuration | |
| 137 //! | |
| 138 //! There are a few different PAM implementations available. By default, | |
| 139 //! nonstick detects which implementation it should use for the current target. | |
| 140 //! If you need to choose a different implementation, set the `LIBPAMSYS_IMPL` | |
| 141 //! environment variable at build time. See the [`libpam_sys`] documentation. | |
| 142 #![doc = concat!("This documentation was built for **", pam_impl_name!(), "**.")] | |
| 143 //! | |
| 144 //! # References | |
| 145 //! | |
| 146 //! - The Linux-PAM guides provide information for a variety of audiences. | |
| 147 //! While some of it is specific to Linux-PAM, much of it applies to other | |
| 148 //! PAM implementations: | |
| 149 //! - [Application Developers' Guide][adg] | |
| 150 //! - [Module Writers' Guide][mwg] | |
| 151 //! - [System Administrators' Guide][sag] | |
| 152 //! - PAM framework man page for developers: | |
| 153 //! - [Linux-PAM developer man page][man7] | |
| 154 //! - [OpenPAM developer man page][manbsd] | |
| 155 //! - [Illumos PAM developer man page][mansun] | |
| 156 //! - PAM framework man page for system administrators: | |
| 157 //! - [Linux-PAM admin documentation][man7pam8] | |
| 158 //! - [OpenPAM admin documentation][bsdpam8] | |
| 159 //! - [Illumos pam.conf documentation][sunpam5] | |
| 160 //! - [The original PAM specification][spec] (mostly of historical interest) | |
| 161 #![doc = crate::_doc::man7!(man7pam8: 8 pam)] | |
| 162 #![doc = crate::_doc::manbsd!(bsdpam8: 8 pam)] | |
| 163 #![doc = crate::_doc::mansun!(sunpam5: 5 "pam.conf")] | |
| 164 #![doc = crate::_doc::stdlinks!(3 pam)] | |
| 165 #![doc = crate::_doc::guide!(adg: "Linux-PAM_ADG.html")] | |
| 166 #![doc = crate::_doc::guide!(mwg: "Linux-PAM_MWG.html")] | |
| 167 #![doc = crate::_doc::guide!(sag: "Linux-PAM_SAG.html")] | |
| 168 #![doc = crate::_doc::xsso!(spec: "toc.htm")] | |
| 24 | 169 |
| 25 #[cfg(feature = "link")] | 170 #[cfg(feature = "link")] |
| 26 mod _compat_checker { | 171 mod _compat_checker { |
| 27 macro_rules! feature_check { | 172 macro_rules! feature_check { |
| 28 ($feature:literal, pam_impl = ($($pimpl:literal),*)) => { | 173 ($feature:literal, pam_impl = ($($pimpl:literal),*)) => { |
| 71 conv::{BinaryData, Conversation, ConversationAdapter}, | 216 conv::{BinaryData, Conversation, ConversationAdapter}, |
| 72 environ::{EnvironMap, EnvironMapMut}, | 217 environ::{EnvironMap, EnvironMapMut}, |
| 73 handle::{ModuleClient, PamShared, Transaction}, | 218 handle::{ModuleClient, PamShared, Transaction}, |
| 74 module::PamModule, | 219 module::PamModule, |
| 75 }; | 220 }; |
| 221 use libpam_sys::pam_impl_name; |
