Mercurial > crates > nonstick
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 } |
