comparison src/handle.rs @ 70:9f8381a1c09c

Implement low-level conversation primitives. This change does two primary things: 1. Introduces new Conversation traits, to be implemented both by the library and by PAM client applications. 2. Builds the memory-management infrastructure for passing messages through the conversation. ...and it adds tests for both of the above, including ASAN tests.
author Paul Fisher <paul@pfish.zone>
date Tue, 03 Jun 2025 01:21:59 -0400
parents 8f3ae0c7ab92
children 58f9d2a4df38
comparison
equal deleted inserted replaced
69:8f3ae0c7ab92 70:9f8381a1c09c
20 /// 20 ///
21 /// # Example 21 /// # Example
22 /// 22 ///
23 /// ```no_run 23 /// ```no_run
24 /// # use nonstick::PamHandle; 24 /// # use nonstick::PamHandle;
25 /// # fn _doc(handle: &impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { 25 /// # fn _doc(handle: &mut impl PamHandle) -> Result<(), Box<dyn std::error::Error>> {
26 /// // Get the username using the default prompt. 26 /// // Get the username using the default prompt.
27 /// let user = handle.get_user(None)?; 27 /// let user = handle.get_user(None)?;
28 /// // Get the username using a custom prompt. 28 /// // Get the username using a custom prompt.
29 /// let user = handle.get_user(Some("who ARE you even???"))?; 29 /// let user = handle.get_user(Some("who ARE you even???"))?;
30 /// # Ok(()) 30 /// # Ok(())
31 /// # } 31 /// # }
32 /// ``` 32 /// ```
33 /// 33 ///
34 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html 34 /// [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 35 /// [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(&self, prompt: Option<&str>) -> Result<String>; 36 fn get_user(&mut self, prompt: Option<&str>) -> Result<String>;
37 37
38 /// Retrieves the authentication token from the user. 38 /// Retrieves the authentication token from the user.
39 /// 39 ///
40 /// This is essentially like `handle.get_item::<Item::AuthTok>()`. 40 /// This is essentially like `handle.get_item::<Item::AuthTok>()`.
41 /// 41 ///
44 /// 44 ///
45 /// # Example 45 /// # Example
46 /// 46 ///
47 /// ```no_run 47 /// ```no_run
48 /// # use nonstick::PamHandle; 48 /// # use nonstick::PamHandle;
49 /// # fn _doc(handle: &impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { 49 /// # fn _doc(handle: &mut impl PamHandle) -> Result<(), Box<dyn std::error::Error>> {
50 /// // Get the user's password using the default prompt. 50 /// // Get the user's password using the default prompt.
51 /// let pass = handle.get_authtok(None)?; 51 /// let pass = handle.get_authtok(None)?;
52 /// // Get the user's password using a custom prompt. 52 /// // Get the user's password using a custom prompt.
53 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; 53 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?;
54 /// Ok(()) 54 /// Ok(())
55 /// # } 55 /// # }
56 /// ``` 56 /// ```
57 /// 57 ///
58 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html 58 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html
59 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item 59 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item
60 fn get_authtok(&self, prompt: Option<&str>) -> Result<SecureString>; 60 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<SecureString>;
61 61
62 /// Retrieves an [Item] that has been set, possibly by the PAM client. 62 /// Retrieves an [Item] that has been set, possibly by the PAM client.
63 /// 63 ///
64 /// These items are *references to PAM memory* 64 /// These items are *references to PAM memory*
65 /// which are *owned by the PAM session* 65 /// which are *owned by the PAM session*
72 /// 72 ///
73 /// ```no_run 73 /// ```no_run
74 /// # use nonstick::PamHandle; 74 /// # use nonstick::PamHandle;
75 /// use nonstick::items::Service; 75 /// use nonstick::items::Service;
76 /// 76 ///
77 /// # fn _doc(pam_handle: &impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { 77 /// # fn _doc(pam_handle: &mut impl PamHandle) -> Result<(), Box<dyn std::error::Error>> {
78 /// let svc: Option<Service> = pam_handle.get_item()?; 78 /// let svc: Option<Service> = pam_handle.get_item()?;
79 /// match svc { 79 /// match svc {
80 /// Some(name) => eprintln!("The calling service name is {:?}", name.to_string_lossy()), 80 /// Some(name) => eprintln!("The calling service name is {:?}", name.to_string_lossy()),
81 /// None => eprintln!("Who knows what the calling service is?"), 81 /// None => eprintln!("Who knows what the calling service is?"),
82 /// } 82 /// }
84 /// # } 84 /// # }
85 /// ``` 85 /// ```
86 /// 86 ///
87 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html 87 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html
88 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item 88 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item
89 fn get_item<T: Item>(&self) -> Result<Option<T>>; 89 fn get_item<T: Item>(&mut self) -> Result<Option<T>>;
90 90
91 /// Sets an item in the PAM context. It can be retrieved using [`get_item`](Self::get_item). 91 /// Sets an item in the PAM context. It can be retrieved using [`get_item`](Self::get_item).
92 /// 92 ///
93 /// See the [`pam_set_item` manual page][man] 93 /// See the [`pam_set_item` manual page][man]
94 /// or [`pam_set_item` in the Module Writer's Guide][mwg]. 94 /// or [`pam_set_item` in the Module Writer's Guide][mwg].
152 /// otherwise you'll get back a completely invalid `&T` 152 /// otherwise you'll get back a completely invalid `&T`
153 /// and further behavior is undefined. 153 /// and further behavior is undefined.
154 /// 154 ///
155 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html 155 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html
156 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data 156 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data
157 unsafe fn get_data<T>(&self, key: &str) -> Result<Option<&T>>; 157 unsafe fn get_data<T>(&mut self, key: &str) -> Result<Option<&T>>;
158 158
159 /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data). 159 /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data).
160 /// 160 ///
161 /// This data is accessible to this module and other PAM modules 161 /// This data is accessible to this module and other PAM modules
162 /// (using the provided `key`), but is *not* accessible to the application. 162 /// (using the provided `key`), but is *not* accessible to the application.
199 } 199 }
200 } 200 }
201 } 201 }
202 202
203 impl PamHandle for LibPamHandle { 203 impl PamHandle for LibPamHandle {
204 fn get_user(&self, prompt: Option<&str>) -> crate::Result<String> { 204 fn get_user(&mut self, prompt: Option<&str>) -> crate::Result<String> {
205 let prompt = memory::option_cstr(prompt)?; 205 let prompt = memory::option_cstr(prompt)?;
206 let mut output: *const c_char = std::ptr::null_mut(); 206 let mut output: *const c_char = std::ptr::null_mut();
207 let ret = unsafe { 207 let ret = unsafe {
208 pam_ffi::pam_get_user(&self.0, &mut output, memory::prompt_ptr(prompt.as_ref())) 208 pam_ffi::pam_get_user(&self.0, &mut output, memory::prompt_ptr(prompt.as_ref()))
209 }; 209 };
210 ErrorCode::result_from(ret)?; 210 ErrorCode::result_from(ret)?;
211 memory::copy_pam_string(output) 211 memory::copy_pam_string(output)
212 } 212 }
213 213
214 fn get_authtok(&self, prompt: Option<&str>) -> crate::Result<SecureString> { 214 fn get_authtok(&mut self, prompt: Option<&str>) -> crate::Result<SecureString> {
215 let prompt = memory::option_cstr(prompt)?; 215 let prompt = memory::option_cstr(prompt)?;
216 let mut output: *const c_char = std::ptr::null_mut(); 216 let mut output: *const c_char = std::ptr::null_mut();
217 let res = unsafe { 217 let res = unsafe {
218 pam_ffi::pam_get_authtok( 218 pam_ffi::pam_get_authtok(
219 &self.0, 219 &self.0,
224 }; 224 };
225 ErrorCode::result_from(res)?; 225 ErrorCode::result_from(res)?;
226 memory::copy_pam_string(output).map(SecureString::from) 226 memory::copy_pam_string(output).map(SecureString::from)
227 } 227 }
228 228
229 fn get_item<T: Item>(&self) -> crate::Result<Option<T>> { 229 fn get_item<T: Item>(&mut self) -> crate::Result<Option<T>> {
230 let mut ptr: *const libc::c_void = std::ptr::null(); 230 let mut ptr: *const libc::c_void = std::ptr::null();
231 let out = unsafe { 231 let out = unsafe {
232 let ret = pam_ffi::pam_get_item(&self.0, T::type_id().into(), &mut ptr); 232 let ret = pam_ffi::pam_get_item(&self.0, T::type_id().into(), &mut ptr);
233 ErrorCode::result_from(ret)?; 233 ErrorCode::result_from(ret)?;
234 let typed_ptr: *const T::Raw = ptr.cast(); 234 let typed_ptr: *const T::Raw = ptr.cast();
257 ErrorCode::result_from(result) 257 ErrorCode::result_from(result)
258 } 258 }
259 } 259 }
260 260
261 impl PamModuleHandle for LibPamHandle { 261 impl PamModuleHandle for LibPamHandle {
262 unsafe fn get_data<T>(&self, key: &str) -> crate::Result<Option<&T>> { 262 unsafe fn get_data<T>(&mut self, key: &str) -> crate::Result<Option<&T>> {
263 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; 263 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?;
264 let mut ptr: *const libc::c_void = std::ptr::null(); 264 let mut ptr: *const libc::c_void = std::ptr::null();
265 ErrorCode::result_from(pam_ffi::pam_get_data(&self.0, c_key.as_ptr(), &mut ptr))?; 265 ErrorCode::result_from(pam_ffi::pam_get_data(&self.0, c_key.as_ptr(), &mut ptr))?;
266 match ptr.is_null() { 266 match ptr.is_null() {
267 true => Ok(None), 267 true => Ok(None),