comparison src/handle.rs @ 146:1bc52025156b

Split PAM items into their own separate struct. To trim down the number of methods on `PamShared`, this puts all the Items into their own struct(s). This also makes the split between authtok/authtok_item easier to understand.
author Paul Fisher <paul@pfish.zone>
date Sun, 06 Jul 2025 19:10:26 -0400
parents 56b559b7ecea
children 3036f2e6a022
comparison
equal deleted inserted replaced
145:8f964b701652 146:1bc52025156b
1 //! The wrapper types and traits for handles into the PAM library. 1 //! The wrapper types and traits for handles into the PAM library.
2 2
3 use crate::_doc::{guide, linklist, man7, manbsd, stdlinks};
3 use crate::constants::{Flags, Result}; 4 use crate::constants::{Flags, Result};
4 use crate::conv::Conversation; 5 use crate::conv::Conversation;
5 use crate::environ::{EnvironMap, EnvironMapMut}; 6 use crate::environ::{EnvironMap, EnvironMapMut};
7 use crate::items::{getter, Items, ItemsMut};
6 use crate::logging::{Level, Location}; 8 use crate::logging::{Level, Location};
7 use crate::{guide, linklist, man7, manbsd, stdlinks};
8 use std::ffi::{OsStr, OsString}; 9 use std::ffi::{OsStr, OsString};
9
10 macro_rules! trait_item {
11 ($(#[$md:meta])* get = $getter:ident, item = $item:literal $(, see = $see:path)?) => {
12 $(#[$md])*
13 #[doc = ""]
14 #[doc = concat!("Gets the `", $item, "` of the PAM handle.")]
15 $(
16 #[doc = concat!("See [`", stringify!($see), "`].")]
17 )?
18 ///
19 /// Returns a reference to the item's value, owned by PAM.
20 /// The item is assumed to be valid UTF-8 text.
21 /// If it is not, `ConversationError` is returned.
22 ///
23 /// # References
24 ///
25 #[doc = linklist!(pam_get_item: mwg, adg, _std)]
26 ///
27 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_get_item")]
28 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#mwg-pam_get_item")]
29 #[doc = stdlinks!(3 pam_get_item)]
30 fn $getter(&self) -> Result<Option<OsString>>;
31 };
32 ($(#[$md:meta])* set = $setter:ident, item = $item:literal $(, see = $see:path)?) => {
33 $(#[$md])*
34 #[doc = ""]
35 #[doc = concat!("Sets the `", $item, "` from the PAM handle.")]
36 $(
37 #[doc = concat!("See [`", stringify!($see), "`].")]
38 )?
39 ///
40 /// Sets the item's value. PAM copies the string's contents.
41 ///
42 /// # Panics
43 ///
44 /// If the string contains a nul byte, this will panic.
45 ///
46 /// # References
47 ///
48 #[doc = linklist!(pam_set_item: mwg, adg, _std)]
49 ///
50 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_set_item")]
51 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#mwg-pam_set_item")]
52 #[doc = stdlinks!(3 pam_set_item)]
53 fn $setter(&mut self, value: Option<&OsStr>) -> Result<()>;
54 };
55 }
56 10
57 /// Functionality for both PAM applications and PAM modules. 11 /// Functionality for both PAM applications and PAM modules.
58 /// 12 ///
59 /// This base trait includes features of a PAM handle that are available 13 /// This base trait includes features of a PAM handle that are available
60 /// to both applications and modules. 14 /// to both applications and modules.
125 /// ``` 79 /// ```
126 #[doc = stdlinks!(3 pam_get_user)] 80 #[doc = stdlinks!(3 pam_get_user)]
127 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#mwg-pam_get_user")] 81 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#mwg-pam_get_user")]
128 fn username(&mut self, prompt: Option<&OsStr>) -> Result<OsString>; 82 fn username(&mut self, prompt: Option<&OsStr>) -> Result<OsString>;
129 83
130 /// The contents of the environment to set, read-only. 84 /// The contents of the environment to set for the logged-in user.
85 ///
86 /// # References
87 ///
88 #[doc = linklist!(pam_getenv: adg, mwg, _std)]
89 ///
90 #[doc = stdlinks!(3 pam_getenv)]
91 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_getenv")]
92 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#adg-pam_getenv")]
131 fn environ(&self) -> impl EnvironMap; 93 fn environ(&self) -> impl EnvironMap;
132 94
133 /// A writable version of the environment. 95 /// A writable map of the environment to set for the logged-in user.
96 ///
97 /// # References
98 ///
99 #[doc = linklist!(pam_putenv: adg, mwg, _std)]
100 ///
101 #[doc = stdlinks!(3 pam_putenv)]
102 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_putenv")]
103 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#adg-pam_putenv")]
134 fn environ_mut(&mut self) -> impl EnvironMapMut; 104 fn environ_mut(&mut self) -> impl EnvironMapMut;
135 105
136 trait_item!( 106 /// Gets Items, data shared by PAM, the application, and modules.
137 /// The identity of the user for whom service is being requested. 107 ///
138 /// 108 /// Certain Items should not be accessed by a PAM application;
139 /// Unlike [`username`](Self::username), this will simply get 109 /// those are available directly on [`ModuleClient`] for use
140 /// the current state of the user item, and not request the username. 110 /// by PAM modules only.
141 /// While PAM usually sets this automatically in the `username` call, 111 ///
142 /// it may be changed by a module during the PAM transaction. 112 /// # References
143 /// Applications should check it after each step of the PAM process. 113 ///
144 get = user_item, 114 #[doc = linklist!(pam_get_item: mwg, adg, _std)]
145 item = "PAM_USER", 115 ///
146 see = Self::username 116 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_get_item")]
147 ); 117 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#mwg-pam_get_item")]
148 trait_item!( 118 #[doc = stdlinks!(3 pam_get_item)]
149 /// Sets the identity of the logging-in user. 119 fn items(&self) -> impl Items;
150 /// 120
151 /// Usually this will be set during the course of 121 /// Read-write access to PAM Items.
152 /// a [`username`](Self::username) call, but you may set it manually 122 ///
153 /// or change it during the PAM process. 123 /// # References
154 set = set_user_item, 124 ///
155 item = "PAM_USER", 125 #[doc = linklist!(pam_set_item: mwg, adg, _std)]
156 see = Self::user_item 126 ///
157 ); 127 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_set_item")]
158 128 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#mwg-pam_set_item")]
159 trait_item!( 129 #[doc = stdlinks!(3 pam_set_item)]
160 /// The service name, which identifies the PAM stack which is used 130 fn items_mut(&mut self) -> impl ItemsMut;
161 /// to perform authentication.
162 get = service,
163 item = "PAM_SERVICE"
164 );
165 trait_item!(
166 /// Sets the service name. It's probably a bad idea to change this.
167 set = set_service,
168 item = "PAM_SERVICE",
169 see = Self::service
170 );
171
172 trait_item!(
173 /// The string used to prompt for a user's name.
174 /// By default, this is a localized version of `login: `.
175 get = user_prompt,
176 item = "PAM_USER_PROMPT"
177 );
178 trait_item!(
179 /// Sets the string used to prompt for a user's name.
180 set = set_user_prompt,
181 item = "PAM_USER_PROMPT",
182 see = Self::user_prompt
183 );
184
185 trait_item!(
186 /// The device path of the TTY being used to log in.
187 ///
188 /// This is the terminal the user is logging in on,
189 /// specified as the full device path (e.g. `/dev/tty0`).
190 /// Very old applications may use this instead of `PAM_XDISPLAY`.
191 get = tty_name,
192 item = "PAM_TTY"
193 );
194 trait_item!(
195 /// Sets the path to the terminal where the user is logging on.
196 set = set_tty_name,
197 item = "PAM_TTY",
198 see = Self::tty_name
199 );
200
201 trait_item!(
202 /// If set, the identity of the remote user logging in.
203 ///
204 /// This is only as trustworthy as the application calling PAM.
205 get = remote_user,
206 item = "PAM_RUSER",
207 see = Self::remote_host
208 );
209 trait_item!(
210 /// Sets the identity of the remote user logging in.
211 ///
212 /// This may be set by the application before making calls
213 /// into a PAM transaction.
214 set = set_remote_user,
215 item = "PAM_RUSER",
216 see = Self::remote_user
217 );
218
219 trait_item!(
220 /// If set, the remote location where the user is coming from.
221 ///
222 /// This is only as trustworthy as the application calling PAM.
223 /// This can be combined with [`Self::remote_user`] to identify
224 /// the account the user is attempting to log in from,
225 /// with `remote_user@remote_host`.
226 ///
227 /// If unset, "it is unclear where the authentication request
228 /// is originating from."
229 get = remote_host,
230 item = "PAM_RHOST",
231 see = Self::remote_user
232 );
233 trait_item!(
234 /// Sets the location where the user is coming from.
235 ///
236 /// This may be set by the application before making calls
237 /// into a PAM transaction.
238 set = set_remote_host,
239 item = "PAM_RHOST",
240 see = Self::remote_host
241 );
242
243 trait_item!(
244 /// Gets the user's authentication token (e.g., password).
245 ///
246 /// This is usually set automatically when
247 /// [`authtok`](PamHandleModule::authtok) is called,
248 /// but can be manually set.
249 set = set_authtok_item,
250 item = "PAM_AUTHTOK",
251 see = PamHandleModule::authtok_item
252 );
253
254 trait_item!(
255 /// Sets the user's "old authentication token" when changing passwords.
256 ///
257 /// This is usually set automatically by PAM.
258 set = set_old_authtok_item,
259 item = "PAM_OLDAUTHTOK",
260 see = PamHandleModule::old_authtok_item
261 );
262 } 131 }
263 132
264 /// Functionality of a PAM handle that can be expected by a PAM application. 133 /// Functionality of a PAM handle that can be expected by a PAM application.
265 /// 134 ///
266 /// If you are not writing a PAM client application (e.g., you are writing 135 /// If you are not writing a PAM client application (e.g., you are writing
314 /// If you are not writing a PAM module (e.g., you are writing an application), 183 /// If you are not writing a PAM module (e.g., you are writing an application),
315 /// you should not use any of the functionality exposed by this trait. 184 /// you should not use any of the functionality exposed by this trait.
316 /// 185 ///
317 /// Like [`PamShared`], this is intended to allow creating mock implementations 186 /// Like [`PamShared`], this is intended to allow creating mock implementations
318 /// of PAM for testing PAM modules. 187 /// of PAM for testing PAM modules.
319 pub trait PamHandleModule: Conversation + PamShared { 188 pub trait ModuleClient: Conversation + PamShared {
320 /// Retrieves the authentication token from the user. 189 /// Retrieves the authentication token from the user.
321 /// 190 ///
322 /// This should only be used by *authentication* and *password-change* 191 /// This should only be used by *authentication* and *password-change*
323 /// PAM modules. 192 /// PAM modules.
324 /// 193 ///
327 #[doc = linklist!(pam_get_authtok: man7, manbsd)] 196 #[doc = linklist!(pam_get_authtok: man7, manbsd)]
328 /// 197 ///
329 /// # Example 198 /// # Example
330 /// 199 ///
331 /// ```no_run 200 /// ```no_run
332 /// # use nonstick::handle::PamHandleModule; 201 /// # use nonstick::handle::ModuleClient;
333 /// # fn _doc(handle: &mut impl PamHandleModule) -> Result<(), Box<dyn std::error::Error>> { 202 /// # fn _doc(handle: &mut impl ModuleClient) -> Result<(), Box<dyn std::error::Error>> {
334 /// // Get the user's password using the default prompt. 203 /// // Get the user's password using the default prompt.
335 /// let pass = handle.authtok(None)?; 204 /// let pass = handle.authtok(None)?;
336 /// // Get the user's password using a custom prompt. 205 /// // Get the user's password using a custom prompt.
337 /// let pass = handle.authtok(Some("Reveal your secrets!".as_ref()))?; 206 /// let pass = handle.authtok(Some("Reveal your secrets!".as_ref()))?;
338 /// Ok(()) 207 /// Ok(())
342 #[doc = manbsd!(3 pam_get_authtok)] 211 #[doc = manbsd!(3 pam_get_authtok)]
343 fn authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString>; 212 fn authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString>;
344 213
345 /// Retrieves the user's old authentication token when changing passwords. 214 /// Retrieves the user's old authentication token when changing passwords.
346 /// 215 ///
347 /// 216 /// This should only be used by a *password-change* module.
217 ///
218 /// # References
219 ///
220 #[doc = linklist!(pam_get_authtok: man7, manbsd)]
221 ///
222 /// # Example
223 ///
224 /// ```no_run
225 /// # use nonstick::handle::ModuleClient;
226 /// # fn _doc(handle: &mut impl ModuleClient) -> Result<(), Box<dyn std::error::Error>> {
227 /// // Get the user's password using the default prompt.
228 /// let pass = handle.old_authtok(None)?;
229 /// // Get the user's password using a custom prompt.
230 /// let pass = handle.old_authtok(Some("Reveal your secrets!".as_ref()))?;
231 /// Ok(())
232 /// # }
233 /// ```
234 ///
235 #[doc = stdlinks!(3 pam_get_authtok)]
348 fn old_authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString>; 236 fn old_authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString>;
349 237
350 trait_item!( 238 getter!(
351 /// Gets the user's authentication token (e.g., password). 239 /// Gets the user's authentication token (e.g., password).
352 /// 240 ///
353 /// This is normally set automatically by PAM when calling 241 /// This is normally set automatically by PAM through [`Self::authtok`],
354 /// [`authtok`](Self::authtok), but can be set explicitly. 242 /// but this will get its value (if set) without prompting the user.
355 /// 243 ///
356 /// Like `authtok`, this should only ever be called 244 /// Like `authtok`, this should only ever be called
357 /// by *authentication* and *password-change* PAM modules. 245 /// by *authentication* and *password-change* PAM modules.
358 get = authtok_item, 246 ///
359 item = "PAM_AUTHTOK", 247 /// # References
360 see = Self::authtok 248 ///
249 #[doc = linklist!(pam_set_item: mwg, adg, _std)]
250 ///
251 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_set_item")]
252 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#mwg-pam_set_item")]
253 #[doc = stdlinks!(3 pam_set_item)]
254 authtok_item("PAM_AUTHTOK", see = Self::authtok)
361 ); 255 );
362 256
363 trait_item!( 257 getter!(
364 /// Gets the user's old authentication token when changing passwords. 258 /// Gets the user's old authentication token when changing passwords.
365 /// 259 ///
366 /// This is normally set automatically by PAM when calling 260 /// This is normally set automatically by PAM through
367 /// [`old_authtok`](Self::old_authtok), but can be set explicitly. 261 /// [`Self::old_authtok`], but this will get its value (if set)
262 /// without prompting the user.
368 /// 263 ///
369 /// This should only ever be called by *password-change* PAM modules. 264 /// This should only ever be called by *password-change* PAM modules.
370 get = old_authtok_item, 265 ///
371 item = "PAM_OLDAUTHTOK", 266 /// # References
372 see = PamShared::set_old_authtok_item 267 ///
268 #[doc = linklist!(pam_set_item: mwg, adg, _std)]
269 ///
270 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_set_item")]
271 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#mwg-pam_set_item")]
272 #[doc = stdlinks!(3 pam_set_item)]
273 old_authtok_item("PAM_OLDAUTHTOK", see = ItemsMut::set_old_authtok)
373 ); 274 );
374 } 275 }