Mercurial > crates > nonstick
comparison src/handle.rs @ 153:3036f2e6a022
Add module-specific data support.
This adds support for a safe form of `pam_get_data` and `pam_set_data`,
where data is (as best as humanly possible) type-safe and restricted
to only the module where it was created.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 08 Jul 2025 00:31:54 -0400 |
parents | 1bc52025156b |
children |
comparison
equal
deleted
inserted
replaced
152:4d11b2e7da83 | 153:3036f2e6a022 |
---|---|
233 /// ``` | 233 /// ``` |
234 /// | 234 /// |
235 #[doc = stdlinks!(3 pam_get_authtok)] | 235 #[doc = stdlinks!(3 pam_get_authtok)] |
236 fn old_authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString>; | 236 fn old_authtok(&mut self, prompt: Option<&OsStr>) -> Result<OsString>; |
237 | 237 |
238 /// Gets an item of module-specific data stored over the transaction. | |
239 /// | |
240 /// This gives you a reference to the data that was earlier set with | |
241 /// [`Self::set_module_data`]. If not present, you get `None`. | |
242 /// | |
243 /// Data is in a module-specific, type-specific namespace. | |
244 /// | |
245 /// ``` | |
246 /// # use nonstick::ModuleClient; | |
247 /// # use std::path::PathBuf; | |
248 /// # fn test(client: &impl ModuleClient) { | |
249 /// // These two can coexist and do not overlap. | |
250 /// let str_data: Option<&String> = client.get_module_data("the_key"); | |
251 /// let num_data: Option<&u64> = client.get_module_data("the_key"); | |
252 /// // ... | |
253 /// let nothing_data: Option<&PathBuf> = client.get_module_data("this does not exist"); | |
254 /// # } | |
255 /// ``` | |
256 /// | |
257 /// # References | |
258 /// | |
259 #[doc = linklist!(pam_get_data: mwg, _std)] | |
260 /// | |
261 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#mwg-pam_get_data")] | |
262 #[doc = stdlinks!(3 pam_get_data)] | |
263 | |
264 fn get_module_data<T: 'static>(&self, key: &str) -> Option<&T>; | |
265 | |
266 /// Sets module-specific data. | |
267 /// | |
268 /// A PAM module may need to store data across multiple invocations within | |
269 /// the same PAM transaction. For instance, a module that stores credentials | |
270 /// would need to know where those credentials were stored in order to | |
271 /// update or destroy them later. Also see [`Self::get_module_data`]. | |
272 /// | |
273 /// Module-specific data gives a module a way to store such data. | |
274 /// Data are stored in a module-specific, type-specific namespace. | |
275 /// | |
276 /// PAM takes ownership of the data passed in. See the **Cleanup** section | |
277 /// below for details on how cleanup is handled. | |
278 /// | |
279 /// # Examples | |
280 /// | |
281 /// Each type of data is in a separate namespace: | |
282 /// | |
283 /// ``` | |
284 /// # use nonstick::{ModuleClient, Result}; | |
285 /// # fn test(client: &mut impl ModuleClient) -> Result<()> { | |
286 /// client.set_module_data("count", 999i32)?; | |
287 /// | |
288 /// let count_int: Option<&i32> = client.get_module_data("count"); | |
289 /// // count_int = Some(&999i32) | |
290 /// let count_string: Option<&String> = client.get_module_data("count"); | |
291 /// // count_string = None | |
292 /// # Ok(()) | |
293 /// # } | |
294 /// ``` | |
295 /// | |
296 /// Data persist across invocations of the same module: | |
297 /// | |
298 /// ``` | |
299 /// # use nonstick::{ModuleClient, Result}; | |
300 /// // In a pam_authenticate call, this function is called: | |
301 /// fn authenticate(client: &mut impl ModuleClient) -> Result<()> { | |
302 /// client.set_module_data::<u64>("TOKEN_ID", 0x0fa1afe10000beef)?; | |
303 /// Ok(()) | |
304 /// } | |
305 /// | |
306 /// // Later, in a pam_session_start call: | |
307 /// fn start_session(client: &mut impl ModuleClient) -> Result<()> { | |
308 /// match client.get_module_data::<u64>("TOKEN_ID") { | |
309 /// Some(&tid) => { | |
310 /// // This will execute and tid will be 0x0fa1afe10000beef. | |
311 /// }, | |
312 /// None => { /* This will not execute. */ }, | |
313 /// } | |
314 /// Ok(()) | |
315 /// } | |
316 /// ``` | |
317 /// | |
318 /// Each module has its own set of data: | |
319 /// | |
320 /// ``` | |
321 /// # use nonstick::{ModuleClient, Result}; | |
322 /// // This function is called somewhere in pam_module_a.so. | |
323 /// fn in_pam_module_a(client: &mut impl ModuleClient) -> Result<()> { | |
324 /// client.set_module_data("value", String::from("pam_module_a data"))?; | |
325 /// Ok(()) | |
326 /// } | |
327 /// | |
328 /// // This function is called later in pam_module_b.so. | |
329 /// fn in_pam_module_b(client: &mut impl ModuleClient) -> Result<()> { | |
330 /// match client.get_module_data::<String>("value") { | |
331 /// Some(value) => { | |
332 /// // This will match, because pam_module_a's data | |
333 /// // is completely unrelated to pam_module_b's data. | |
334 /// }, | |
335 /// None => { | |
336 /// // This branch will execute. | |
337 /// }, | |
338 /// } | |
339 /// // ... | |
340 /// # Ok(()) | |
341 /// } | |
342 /// ``` | |
343 /// | |
344 /// # Cleanup | |
345 /// | |
346 /// PAM modules should be careful about cleaning up data outside their own | |
347 /// address space, because PAM applications may `fork()`: | |
348 /// | |
349 /// ```plain | |
350 /// ┃ let tx = start_pam_transaction(); | |
351 /// ┃ | |
352 /// ┃ tx.authenticate(); | |
353 /// ┃ │ // PAM calls into your module where you set data: | |
354 /// ┃ │ handle.set_module_data("key", the_data); | |
355 /// ┃ | |
356 /// ┃ fork(); | |
357 /// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━┓ | |
358 /// Parent process Child process | |
359 /// ┃ wait(child); ┃ setuid(user_to_login); | |
360 /// ┃ ┆ ┃ // ... do other stuff ... | |
361 /// ┃ ┆ ┃ drop(tx); | |
362 /// ┃ ┆ ┃ │ // PAM cleans up your data. | |
363 /// ┃ ┆ ┃ │ drop(the_data); | |
364 /// ┃ ┆ ┗ exec(user's shell) | |
365 /// ┃ ┆ ┃ // user does stuff over their session | |
366 /// ┃ ┆ ┃ // ... | |
367 /// ┃ ┆ X | |
368 /// ┃ | |
369 /// ┃ drop(tx); | |
370 /// ┃ │ // Parent PAM cleans up your data. | |
371 /// ┃ │ drop(the_data); // Called again, but in this process instead! | |
372 /// ``` | |
373 /// | |
374 /// While LibPAM offers a way to customize the action taken on cleanup, | |
375 /// we do not (yet) offer this. | |
376 /// | |
377 /// # References | |
378 /// | |
379 #[doc = linklist!(pam_set_data: mwg, _std)] | |
380 /// | |
381 #[doc = guide!(mwg: "mwg-expected-by-module-item.html#mwg-pam_set_data")] | |
382 #[doc = stdlinks!(3 pam_set_data)] | |
383 fn set_module_data<T: 'static>(&mut self, key: &str, data: T) -> Result<()>; | |
384 | |
238 getter!( | 385 getter!( |
239 /// Gets the user's authentication token (e.g., password). | 386 /// Gets the user's authentication token (e.g., password). |
240 /// | 387 /// |
241 /// This is normally set automatically by PAM through [`Self::authtok`], | 388 /// This is normally set automatically by PAM through [`Self::authtok`], |
242 /// but this will get its value (if set) without prompting the user. | 389 /// but this will get its value (if set) without prompting the user. |