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 | 0099f2f79f86 |
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. |
