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; |