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;