Mercurial > crates > nonstick
comparison src/handle.rs @ 72:47eb242a4f88
Fill out the PamHandle trait.
This updates the PamHandle trait to have methods for each Item,
and implements them on the LibPamHandle.
| author | Paul Fisher <paul@pfish.zone> |
|---|---|
| date | Wed, 04 Jun 2025 03:53:36 -0400 |
| parents | 58f9d2a4df38 |
| children | ac6881304c78 |
comparison
equal
deleted
inserted
replaced
| 71:58f9d2a4df38 | 72:47eb242a4f88 |
|---|---|
| 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 use crate::constants::{ErrorCode, Result}; | 2 use crate::constants::{ErrorCode, Result}; |
| 3 use crate::items::{Item, ItemType}; | 3 use crate::conv::Conversation; |
| 4 use crate::items::ItemType; | |
| 5 use crate::module::ConversationMux; | |
| 4 use crate::pam_ffi; | 6 use crate::pam_ffi; |
| 5 use crate::pam_ffi::memory; | 7 use crate::pam_ffi::{memory, LibPamConversation, LibPamHandle}; |
| 6 use secure_string::SecureString; | 8 use std::ffi::{c_char, c_int}; |
| 7 use std::ffi::{c_char, c_int, c_void, CString}; | |
| 8 use std::{mem, ptr}; | 9 use std::{mem, ptr}; |
| 10 | |
| 11 macro_rules! trait_item { | |
| 12 (get = $getter:ident, item = $item:literal $(, see = $see:path)? $(, $($doc:literal)*)?) => { | |
| 13 $( | |
| 14 $(#[doc = $doc])* | |
| 15 #[doc = ""] | |
| 16 )? | |
| 17 #[doc = concat!("Gets the `", $item, "` of the PAM handle.")] | |
| 18 $( | |
| 19 #[doc = concat!("See [`", stringify!($see), "`].")] | |
| 20 )? | |
| 21 #[doc = ""] | |
| 22 #[doc = "Returns a reference to the item's value, owned by PAM."] | |
| 23 #[doc = "The item is assumed to be valid UTF-8 text."] | |
| 24 #[doc = "If it is not, `ConversationError` is returned."] | |
| 25 #[doc = ""] | |
| 26 #[doc = "See the [`pam_get_item`][man] manual page,"] | |
| 27 #[doc = "[`pam_get_item` in the Module Writers' Guide][mwg], or"] | |
| 28 #[doc = "[`pam_get_item` in the Application Developers' Guide][adg]."] | |
| 29 #[doc = ""] | |
| 30 #[doc = "[man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html"] | |
| 31 #[doc = "[adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_get_item"] | |
| 32 #[doc = "[mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item"] | |
| 33 fn $getter(&mut self) -> Result<Option<&str>>; | |
| 34 }; | |
| 35 (set = $setter:ident, item = $item:literal $(, see = $see:path)? $(, $($doc:literal)*)?) => { | |
| 36 $( | |
| 37 $(#[doc = $doc])* | |
| 38 #[doc = ""] | |
| 39 )? | |
| 40 #[doc = concat!("Sets the `", $item, "` from the PAM handle.")] | |
| 41 $( | |
| 42 #[doc = concat!("See [`", stringify!($see), "`].")] | |
| 43 )? | |
| 44 #[doc = ""] | |
| 45 #[doc = "Sets the item's value. PAM copies the string's contents."] | |
| 46 #[doc = "If the string contains a null byte, this will return "] | |
| 47 #[doc = "a `ConversationError`."] | |
| 48 #[doc = ""] | |
| 49 #[doc = "See the [`pam_set_item`][man] manual page,"] | |
| 50 #[doc = "[`pam_set_item` in the Module Writers' Guide][mwg], or"] | |
| 51 #[doc = "[`pam_set_item` in the Application Developers' Guide][adg]."] | |
| 52 #[doc = ""] | |
| 53 #[doc = "[man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html"] | |
| 54 #[doc = "[adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_set_item"] | |
| 55 #[doc = "[mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item"] | |
| 56 fn $setter(&mut self, value: Option<&str>) -> Result<()>; | |
| 57 }; | |
| 58 } | |
| 9 | 59 |
| 10 /// Features of a PAM handle that are available to applications and modules. | 60 /// Features of a PAM handle that are available to applications and modules. |
| 11 /// | 61 /// |
| 12 /// You probably want [`LibPamHandle`]. This trait is intended to allow creating | 62 /// You probably want [`LibPamHandle`]. This trait is intended to allow creating |
| 13 /// mock PAM handle types used for testing PAM modules and applications. | 63 /// mock PAM handle types used for testing PAM modules and applications. |
| 14 pub trait PamHandle { | 64 pub trait PamHandle { |
| 65 type Conv: Conversation; | |
| 15 /// Retrieves the name of the user who is authenticating or logging in. | 66 /// Retrieves the name of the user who is authenticating or logging in. |
| 16 /// | 67 /// |
| 17 /// This is effectively like `handle.get_item::<Item::User>()`. | 68 /// If the username has previously been obtained, this uses that username; |
| 69 /// otherwise it prompts the user with the first of these that is present: | |
| 70 /// | |
| 71 /// 1. The prompt string passed to this function. | |
| 72 /// 2. The string returned by `get_user_prompt_item`. | |
| 73 /// 3. The default prompt, `login: ` | |
| 74 /// | |
| 18 /// See the [`pam_get_user` manual page][man] | 75 /// See the [`pam_get_user` manual page][man] |
| 19 /// or [`pam_get_user` in the Module Writer's Guide][mwg]. | 76 /// or [`pam_get_user` in the Module Writer's Guide][mwg]. |
| 20 /// | 77 /// |
| 21 /// # Example | 78 /// # Example |
| 22 /// | 79 /// |
| 23 /// ```no_run | 80 /// ```no_run |
| 24 /// # use nonstick::PamHandle; | 81 /// # use nonstick::PamModuleHandle; |
| 25 /// # fn _doc(handle: &mut impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { | 82 /// # fn _doc(handle: &mut impl PamModuleHandle) -> Result<(), Box<dyn std::error::Error>> { |
| 26 /// // Get the username using the default prompt. | 83 /// // Get the username using the default prompt. |
| 27 /// let user = handle.get_user(None)?; | 84 /// let user = handle.get_user(None)?; |
| 28 /// // Get the username using a custom prompt. | 85 /// // Get the username using a custom prompt. |
| 29 /// let user = handle.get_user(Some("who ARE you even???"))?; | 86 /// // If this were actually called right after the above, |
| 87 /// // both user and user_2 would have the same value. | |
| 88 /// let user_2 = handle.get_user(Some("who ARE you even???"))?; | |
| 30 /// # Ok(()) | 89 /// # Ok(()) |
| 31 /// # } | 90 /// # } |
| 32 /// ``` | 91 /// ``` |
| 33 /// | 92 /// |
| 34 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html | 93 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html |
| 35 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user | 94 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user |
| 36 fn get_user(&mut self, prompt: Option<&str>) -> Result<String>; | 95 fn get_user(&mut self, prompt: Option<&str>) -> Result<Option<&str>>; |
| 37 | 96 |
| 38 /// Retrieves the authentication token from the user. | 97 trait_item!( |
| 39 /// | 98 get = user_item, |
| 40 /// This is essentially like `handle.get_item::<Item::AuthTok>()`. | 99 item = "PAM_USER", |
| 41 /// | 100 "The identity of the user for whom service is being requested." |
| 42 /// See the [`pam_get_authtok` manual page][man] | 101 "" |
| 43 /// or [`pam_get_item` in the Module Writer's Guide][mwg]. | 102 "While PAM usually sets this automatically during the course of " |
| 44 /// | 103 "a [`get_user`](Self::get_user) call, it may be changed by a module " |
| 45 /// # Example | 104 "over the course of the PAM transaction." |
| 46 /// | 105 "Applications should check it after each step of the PAM process." |
| 47 /// ```no_run | 106 ); |
| 48 /// # use nonstick::PamHandle; | 107 trait_item!( |
| 49 /// # fn _doc(handle: &mut impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { | 108 set = set_user_item, |
| 50 /// // Get the user's password using the default prompt. | 109 item = "PAM_USER", |
| 51 /// let pass = handle.get_authtok(None)?; | 110 see = Self::user_item, |
| 52 /// // Get the user's password using a custom prompt. | 111 "Sets the identity of the logging-in user." |
| 53 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; | 112 "" |
| 54 /// Ok(()) | 113 "Usually this will be set during the course of " |
| 55 /// # } | 114 "a [`get_user`](Self::get_user) call, but you may set it manually " |
| 56 /// ``` | 115 "or change it during the PAM process." |
| 57 /// | 116 ); |
| 58 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html | 117 |
| 59 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item | 118 trait_item!( |
| 60 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<SecureString>; | 119 get = service, |
| 61 | 120 item = "PAM_SERVICE", |
| 62 /// Retrieves an [Item] that has been set, possibly by the PAM client. | 121 "The service name, which identifies the PAM stack which is used " |
| 63 /// | 122 "to perform authentication." |
| 64 /// These items are *references to PAM memory* | 123 ); |
| 65 /// which are *owned by the PAM session* | 124 trait_item!( |
| 66 /// and you should never modify them. | 125 set = set_service, |
| 67 /// | 126 item = "PAM_SERVICE", |
| 68 /// See the [`pam_get_item` manual page][man] | 127 see = Self::service, |
| 69 /// or [`pam_get_item` in the Module Writer's Guide][mwg]. | 128 "The service name, which identifies the PAM stack which is used " |
| 70 /// | 129 "to perform authentication. It's probably a bad idea to change this." |
| 71 /// # Example | 130 ); |
| 72 /// | 131 |
| 73 /// ```no_run | 132 trait_item!( |
| 74 /// # use nonstick::PamHandle; | 133 get = user_prompt, |
| 75 /// use nonstick::items::Service; | 134 item = "PAM_USER_PROMPT", |
| 76 /// | 135 "The string used to prompt for a user's name." |
| 77 /// # fn _doc(pam_handle: &mut impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { | 136 "By default, this is a localized version of `login: `." |
| 78 /// let svc: Option<Service> = pam_handle.get_item()?; | 137 ); |
| 79 /// match svc { | 138 trait_item!( |
| 80 /// Some(name) => eprintln!("The calling service name is {:?}", name.to_string_lossy()), | 139 set = set_user_prompt, |
| 81 /// None => eprintln!("Who knows what the calling service is?"), | 140 item = "PAM_USER_PROMPT", |
| 82 /// } | 141 see = Self::user_prompt, |
| 83 /// # Ok(()) | 142 "Sets the string used to prompt for a user's name." |
| 84 /// # } | 143 ); |
| 85 /// ``` | 144 |
| 86 /// | 145 trait_item!( |
| 87 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html | 146 get = tty_name, |
| 88 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item | 147 item = "PAM_TTY", |
| 89 fn get_item<T: Item>(&mut self) -> Result<Option<T>>; | 148 "\"The terminal name prefixed by /dev/ for device files.\"" |
| 90 | 149 "" |
| 91 /// Sets an item in the PAM context. It can be retrieved using [`get_item`](Self::get_item). | 150 "This is the terminal the user is logging in on." |
| 92 /// | 151 "Very old applications may use this instead of `PAM_XDISPLAY`." |
| 93 /// See the [`pam_set_item` manual page][man] | 152 ); |
| 94 /// or [`pam_set_item` in the Module Writer's Guide][mwg]. | 153 trait_item!( |
| 95 /// | 154 set = set_tty_name, |
| 96 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html | 155 item = "PAM_TTY", |
| 97 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item | 156 see = Self::tty_name, |
| 98 fn set_item<T: Item>(&mut self, item: T) -> Result<()>; | 157 "Sets the terminal name." |
| 158 "" | |
| 159 "(TODO: See if libpam sets this itself or if the application does.)" | |
| 160 ); | |
| 161 | |
| 162 trait_item!( | |
| 163 get = remote_user, | |
| 164 item = "PAM_RUSER", | |
| 165 "If set, the identity of the remote user logging in." | |
| 166 "" | |
| 167 "This is only as trustworthy as the application calling PAM." | |
| 168 "Also see [`remote_host`](Self::remote_host)." | |
| 169 ); | |
| 170 trait_item!( | |
| 171 set = set_remote_user, | |
| 172 item = "PAM_RUSER", | |
| 173 "Sets the identity of the remote user logging in." | |
| 174 "" | |
| 175 "This is usually set by the application before making calls " | |
| 176 "into a PAM session. (TODO: check this!)" | |
| 177 ); | |
| 178 | |
| 179 trait_item!( | |
| 180 get = remote_host, | |
| 181 item = "PAM_RHOST", | |
| 182 "If set, the remote location where the user is coming from." | |
| 183 "" | |
| 184 "This is only as trustworthy as the application calling PAM. " | |
| 185 "This can be combined with [`Self::remote_user`] to identify " | |
| 186 "the account the user is attempting to log in from, " | |
| 187 "with `remote_user@remote_host`." | |
| 188 "" | |
| 189 "If unset, \"it is unclear where the authentication request " | |
| 190 "is originating from.\"" | |
| 191 ); | |
| 192 trait_item!( | |
| 193 set = set_remote_host, | |
| 194 item = "PAM_RHOST", | |
| 195 see = Self::remote_host, | |
| 196 "Sets the location where the user is coming from." | |
| 197 "" | |
| 198 "This is usually set by the application before making calls " | |
| 199 "into a PAM session. (TODO: check this!)" | |
| 200 ); | |
| 201 | |
| 202 trait_item!( | |
| 203 set = set_authtok_item, | |
| 204 item = "PAM_AUTHTOK", | |
| 205 see = PamModuleHandle::authtok_item, | |
| 206 "Sets the user's authentication token (e.g., password)." | |
| 207 "" | |
| 208 "This is usually set automatically when " | |
| 209 "[`get_authtok`](PamModuleHandle::get_authtok) is called, " | |
| 210 "but can be manually set." | |
| 211 ); | |
| 212 | |
| 213 trait_item!( | |
| 214 set = set_old_authtok_item, | |
| 215 item = "PAM_OLDAUTHTOK", | |
| 216 see = PamModuleHandle::old_authtok_item, | |
| 217 "Sets the user's \"old authentication token\" when changing passwords." | |
| 218 "" | |
| 219 "This is usually set automatically by PAM." | |
| 220 ); | |
| 99 } | 221 } |
| 100 | 222 |
| 101 /// Functionality of a PAM handle that can be expected by a PAM application. | 223 /// Functionality of a PAM handle that can be expected by a PAM application. |
| 102 /// | 224 /// |
| 103 /// If you are not writing a PAM client application (e.g., you are writing | 225 /// If you are not writing a PAM client application (e.g., you are writing |
| 126 /// # } | 248 /// # } |
| 127 /// ``` | 249 /// ``` |
| 128 /// | 250 /// |
| 129 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_end.3.html | 251 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_end.3.html |
| 130 fn close(self, status: Result<()>) -> Result<()>; | 252 fn close(self, status: Result<()>) -> Result<()>; |
| 253 | |
| 254 /// Uses a new PAM conversation. | |
| 255 fn set_conversation(&mut self, conversation: Self::Conv) -> Result<()>; | |
| 131 } | 256 } |
| 132 | 257 |
| 133 /// Functionality of a PAM handle that can be expected by a PAM module. | 258 /// Functionality of a PAM handle that can be expected by a PAM module. |
| 134 /// | 259 /// |
| 135 /// If you are not writing a PAM module (e.g., you are writing an application), | 260 /// If you are not writing a PAM module (e.g., you are writing an application), |
| 136 /// you should not use any of the functionality exposed by this trait. | 261 /// you should not use any of the functionality exposed by this trait. |
| 137 /// | 262 /// |
| 138 /// Like [`PamHandle`], this is intended to allow creating mock implementations | 263 /// Like [`PamHandle`], this is intended to allow creating mock implementations |
| 139 /// of PAM for testing PAM modules. | 264 /// of PAM for testing PAM modules. |
| 140 pub trait PamModuleHandle: PamHandle { | 265 pub trait PamModuleHandle: PamHandle { |
| 141 /// Gets some pointer, identified by `key`, that has been set previously | 266 /// Gets a channel for communication with the user. |
| 142 /// using [`set_data`](Self::set_data). | 267 /// |
| 143 /// | 268 /// The Conversation is the conduit which you use for all communication |
| 144 /// The data, if present, is still owned by the current PAM session. | 269 /// with the user. |
| 145 /// | 270 fn conversation(&mut self) -> Result<ConversationMux<'_, Self::Conv>>; |
| 146 /// See the [`pam_get_data` manual page][man] | 271 |
| 147 /// or [`pam_get_data` in the Module Writer's Guide][mwg]. | 272 /// Retrieves the authentication token from the user. |
| 273 /// | |
| 274 /// This should only be used by *authentication* and *password-change* | |
| 275 /// PAM modules. | |
| 276 /// | |
| 277 /// See the [`pam_get_authtok` manual page][man] | |
| 278 /// or [`pam_get_item` in the Module Writer's Guide][mwg]. | |
| 279 /// | |
| 280 /// # Example | |
| 281 /// | |
| 282 /// ```no_run | |
| 283 /// # use nonstick::PamModuleHandle; | |
| 284 /// # fn _doc(handle: &mut impl PamModuleHandle) -> Result<(), Box<dyn std::error::Error>> { | |
| 285 /// // Get the user's password using the default prompt. | |
| 286 /// let pass = handle.get_authtok(None)?; | |
| 287 /// // Get the user's password using a custom prompt. | |
| 288 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; | |
| 289 /// Ok(()) | |
| 290 /// # } | |
| 291 /// ``` | |
| 292 /// | |
| 293 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html | |
| 294 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item | |
| 295 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<Option<&str>>; | |
| 296 | |
| 297 trait_item!( | |
| 298 get = authtok_item, | |
| 299 item = "PAM_AUTHTOK", | |
| 300 see = Self::get_authtok, | |
| 301 "Gets the user's authentication token (e.g., password)." | |
| 302 "" | |
| 303 "This is normally set automatically by PAM when calling " | |
| 304 "[`get_authtok`](Self::get_authtok), but can be set explicitly." | |
| 305 "" | |
| 306 "Like `get_authtok`, this should only ever be called " | |
| 307 "by *authentication* and *password-change* PAM modules." | |
| 308 ); | |
| 309 | |
| 310 trait_item!( | |
| 311 get = old_authtok_item, | |
| 312 item = "PAM_OLDAUTHTOK", | |
| 313 see = PamHandle::set_old_authtok_item, | |
| 314 "Gets the user's old authentication token when changing passwords." | |
| 315 "" | |
| 316 "This should only ever be called by *password-change* PAM modules." | |
| 317 ); | |
| 318 | |
| 319 /* | |
| 320 TODO: Re-enable this at some point. | |
| 321 /// Gets some pointer, identified by `key`, that has been set previously | |
| 322 /// using [`set_data`](Self::set_data). | |
| 323 /// | |
| 324 /// The data, if present, is still owned by the current PAM session. | |
| 325 /// | |
| 326 /// See the [`pam_get_data` manual page][man] | |
| 327 /// or [`pam_get_data` in the Module Writer's Guide][mwg]. | |
| 328 /// | |
| 329 /// # Safety | |
| 330 /// | |
| 331 /// The data stored under the provided key must be of type `T`, | |
| 332 /// otherwise you'll get back a completely invalid `&T` | |
| 333 /// and further behavior is undefined. | |
| 334 /// | |
| 335 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html | |
| 336 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data | |
| 337 unsafe fn get_data<T>(&mut self, key: &str) -> Result<Option<&T>>; | |
| 338 | |
| 339 /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data). | |
| 340 /// | |
| 341 /// This data is accessible to this module and other PAM modules | |
| 342 /// (using the provided `key`), but is *not* accessible to the application. | |
| 343 /// The PAM session takes ownership of the data, and it will be dropped | |
| 344 /// when the session ends. | |
| 345 /// | |
| 346 /// See the [`pam_set_data` manual page][man] | |
| 347 /// or [`pam_set_data` in the Module Writer's Guide][mwg]. | |
| 348 /// | |
| 349 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html | |
| 350 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_data | |
| 351 fn set_data<T>(&mut self, key: &str, data: Box<T>) -> Result<()>; | |
| 352 */ | |
| 353 } | |
| 354 | |
| 355 | |
| 356 impl LibPamHandle { | |
| 357 /// Gets a C string item. | |
| 148 /// | 358 /// |
| 149 /// # Safety | 359 /// # Safety |
| 150 /// | 360 /// |
| 151 /// The data stored under the provided key must be of type `T`, | 361 /// You better be requesting an item which is a C string. |
| 152 /// otherwise you'll get back a completely invalid `&T` | 362 unsafe fn get_cstr_item(&mut self, item_type: ItemType) -> Result<Option<&str>> { |
| 153 /// and further behavior is undefined. | 363 let mut output = ptr::null(); |
| 154 /// | 364 let ret = unsafe { pam_ffi::pam_get_item(self, item_type as c_int, &mut output) }; |
| 155 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html | 365 ErrorCode::result_from(ret)?; |
| 156 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data | 366 memory::wrap_string(output.cast()) |
| 157 unsafe fn get_data<T>(&mut self, key: &str) -> Result<Option<&T>>; | 367 } |
| 158 | 368 |
| 159 /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data). | 369 /// Sets a C string item. |
| 160 /// | 370 /// |
| 161 /// This data is accessible to this module and other PAM modules | 371 /// # Safety |
| 162 /// (using the provided `key`), but is *not* accessible to the application. | 372 /// |
| 163 /// The PAM session takes ownership of the data, and it will be dropped | 373 /// You better be setting an item which is a C string. |
| 164 /// when the session ends. | 374 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> { |
| 165 /// | 375 let data_str = memory::option_cstr(data)?; |
| 166 /// See the [`pam_set_data` manual page][man] | 376 let ret = unsafe { |
| 167 /// or [`pam_set_data` in the Module Writer's Guide][mwg]. | 377 pam_ffi::pam_set_item( |
| 168 /// | 378 self, |
| 169 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html | 379 item_type as c_int, |
| 170 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_data | 380 memory::prompt_ptr(data_str.as_ref()).cast(), |
| 171 fn set_data<T>(&mut self, key: &str, data: Box<T>) -> Result<()>; | 381 ) |
| 172 } | 382 }; |
| 173 | 383 ErrorCode::result_from(ret) |
| 174 /// A [`PamHandle`] backed by `libpam`, i.e., a real PAM handle. | 384 } |
| 175 /// | 385 } |
| 176 /// This structure wraps an opaque PAM handle and gives you a nice Rusty | |
| 177 /// interface to use PAM. | |
| 178 #[repr(C)] | |
| 179 pub struct LibPamHandle(pam_ffi::Handle); | |
| 180 | 386 |
| 181 impl Drop for LibPamHandle { | 387 impl Drop for LibPamHandle { |
| 182 /// Ends the PAM session with a zero error code. | 388 /// Ends the PAM session with a zero error code. |
| 183 /// You probably want to call [`close`](Self::close) instead of | 389 /// You probably want to call [`close`](Self::close) instead of |
| 184 /// letting this drop by itself. | 390 /// letting this drop by itself. |
| 185 fn drop(&mut self) { | 391 fn drop(&mut self) { |
| 186 unsafe { | 392 unsafe { |
| 187 pam_ffi::pam_end(&mut self.0, 0); | 393 pam_ffi::pam_end(self, 0); |
| 188 } | 394 } |
| 189 } | 395 } |
| 190 } | 396 } |
| 191 | 397 |
| 398 macro_rules! cstr_item { | |
| 399 (get = $getter:ident, item = $item_type:path) => { | |
| 400 fn $getter(&mut self) -> Result<Option<&str>> { | |
| 401 unsafe { self.get_cstr_item($item_type) } | |
| 402 } | |
| 403 }; | |
| 404 (set = $setter:ident, item = $item_type:path) => { | |
| 405 fn $setter(&mut self, value: Option<&str>) -> Result<()> { | |
| 406 unsafe { self.set_cstr_item($item_type, value) } | |
| 407 } | |
| 408 }; | |
| 409 } | |
| 410 | |
| 192 impl PamHandle for LibPamHandle { | 411 impl PamHandle for LibPamHandle { |
| 193 fn get_user(&mut self, prompt: Option<&str>) -> crate::Result<String> { | 412 type Conv = LibPamConversation; |
| 413 fn get_user(&mut self, prompt: Option<&str>) -> Result<Option<&str>> { | |
| 194 let prompt = memory::option_cstr(prompt)?; | 414 let prompt = memory::option_cstr(prompt)?; |
| 195 let mut output: *const c_char = ptr::null_mut(); | 415 let mut output: *const c_char = ptr::null(); |
| 196 let ret = unsafe { | 416 let ret = unsafe { |
| 197 pam_ffi::pam_get_user(&self.0, &mut output, memory::prompt_ptr(prompt.as_ref())) | 417 pam_ffi::pam_get_user(self, &mut output, memory::prompt_ptr(prompt.as_ref())) |
| 198 }; | 418 }; |
| 199 ErrorCode::result_from(ret)?; | 419 ErrorCode::result_from(ret)?; |
| 200 unsafe {memory::copy_pam_string(output)} | 420 unsafe { memory::wrap_string(output) } |
| 201 } | 421 } |
| 202 | 422 |
| 203 fn get_authtok(&mut self, prompt: Option<&str>) -> crate::Result<SecureString> { | 423 cstr_item!(get = user_item, item = ItemType::User); |
| 424 cstr_item!(set = set_user_item, item = ItemType::User); | |
| 425 cstr_item!(get = service, item = ItemType::Service); | |
| 426 cstr_item!(set = set_service, item = ItemType::Service); | |
| 427 cstr_item!(get = user_prompt, item = ItemType::UserPrompt); | |
| 428 cstr_item!(set = set_user_prompt, item = ItemType::UserPrompt); | |
| 429 cstr_item!(get = tty_name, item = ItemType::Tty); | |
| 430 cstr_item!(set = set_tty_name, item = ItemType::Tty); | |
| 431 cstr_item!(get = remote_user, item = ItemType::RemoteUser); | |
| 432 cstr_item!(set = set_remote_user, item = ItemType::RemoteUser); | |
| 433 cstr_item!(get = remote_host, item = ItemType::RemoteHost); | |
| 434 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost); | |
| 435 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); | |
| 436 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); | |
| 437 } | |
| 438 | |
| 439 impl PamApplicationHandle for LibPamHandle { | |
| 440 fn close(mut self, status: Result<()>) -> Result<()> { | |
| 441 let result = unsafe { pam_ffi::pam_end(&mut self, ErrorCode::result_to_c(status)) }; | |
| 442 // Since we've already `pam_end`ed this session, we don't want it to be | |
| 443 // double-freed on drop. | |
| 444 mem::forget(self); | |
| 445 ErrorCode::result_from(result) | |
| 446 } | |
| 447 | |
| 448 fn set_conversation(&mut self, conversation: Self::Conv) -> Result<()> { | |
| 449 todo!() | |
| 450 } | |
| 451 } | |
| 452 | |
| 453 impl PamModuleHandle for LibPamHandle { | |
| 454 fn conversation(&mut self) -> Result<ConversationMux<'_, Self::Conv>> { | |
| 455 todo!() | |
| 456 } | |
| 457 | |
| 458 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<Option<&str>> { | |
| 204 let prompt = memory::option_cstr(prompt)?; | 459 let prompt = memory::option_cstr(prompt)?; |
| 205 let mut output: *const c_char = ptr::null_mut(); | 460 let mut output: *const c_char = ptr::null_mut(); |
| 206 let res = unsafe { | 461 let res = unsafe { |
| 207 pam_ffi::pam_get_authtok( | 462 pam_ffi::pam_get_authtok( |
| 208 &self.0, | 463 self, |
| 209 ItemType::AuthTok.into(), | 464 ItemType::AuthTok.into(), |
| 210 &mut output, | 465 &mut output, |
| 211 memory::prompt_ptr(prompt.as_ref()), | 466 memory::prompt_ptr(prompt.as_ref()), |
| 212 ) | 467 ) |
| 213 }; | 468 }; |
| 214 ErrorCode::result_from(res)?; | 469 ErrorCode::result_from(res)?; |
| 215 unsafe {memory::copy_pam_string(output)}.map(SecureString::from) | 470 unsafe { memory::wrap_string(output) } |
| 216 } | 471 } |
| 217 | 472 |
| 218 fn get_item<T: Item>(&mut self) -> Result<Option<T>> { | 473 cstr_item!(get = authtok_item, item = ItemType::AuthTok); |
| 219 let mut ptr: *const c_void = ptr::null(); | 474 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok); |
| 220 let out = unsafe { | |
| 221 let ret = pam_ffi::pam_get_item(&self.0, T::type_id().into(), &mut ptr); | |
| 222 ErrorCode::result_from(ret)?; | |
| 223 (ptr as *const T::Raw).as_ref().map(|p| T::from_raw(p)) | |
| 224 }; | |
| 225 Ok(out) | |
| 226 } | |
| 227 | |
| 228 fn set_item<T: Item>(&mut self, item: T) -> Result<()> { | |
| 229 let ret = unsafe { | |
| 230 pam_ffi::pam_set_item(&mut self.0, T::type_id().into(), item.into_raw().cast()) | |
| 231 }; | |
| 232 ErrorCode::result_from(ret) | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 impl PamApplicationHandle for LibPamHandle { | |
| 237 fn close(mut self, status: Result<()>) -> Result<()> { | |
| 238 let result = unsafe { pam_ffi::pam_end(&mut self.0, ErrorCode::result_to_c(status)) }; | |
| 239 // Since we've already `pam_end`ed this session, we don't want it to be | |
| 240 // double-freed on drop. | |
| 241 mem::forget(self); | |
| 242 ErrorCode::result_from(result) | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 impl PamModuleHandle for LibPamHandle { | |
| 247 unsafe fn get_data<T>(&mut self, key: &str) -> crate::Result<Option<&T>> { | |
| 248 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; | |
| 249 let mut ptr: *const c_void = ptr::null(); | |
| 250 ErrorCode::result_from(pam_ffi::pam_get_data(&self.0, c_key.as_ptr(), &mut ptr))?; | |
| 251 Ok((ptr as *const T).as_ref()) | |
| 252 } | |
| 253 | |
| 254 fn set_data<T>(&mut self, key: &str, data: Box<T>) -> crate::Result<()> { | |
| 255 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; | |
| 256 let ret = unsafe { | |
| 257 pam_ffi::pam_set_data( | |
| 258 &mut self.0, | |
| 259 c_key.as_ptr(), | |
| 260 Box::into_raw(data).cast(), | |
| 261 set_data_cleanup::<T>, | |
| 262 ) | |
| 263 }; | |
| 264 ErrorCode::result_from(ret) | |
| 265 } | |
| 266 } | 475 } |
| 267 | 476 |
| 268 /// Function called at the end of a PAM session that is called to clean up | 477 /// Function called at the end of a PAM session that is called to clean up |
| 269 /// a value previously provided to PAM in a `pam_set_data` call. | 478 /// a value previously provided to PAM in a `pam_set_data` call. |
| 270 /// | 479 /// |
