Mercurial > crates > nonstick
comparison src/handle.rs @ 66:a674799a5cd3
Make `PamHandle` and `PamModuleHandle` traits.
This creates traits for PAM functionality and pulls the definitions
of that functionality out of the original `PamHandle` (renamed to
`LibPamHandle`) and into those traits. This supports testing PAM
module implementations using mock PAM library implementations.
Also uses a better representation of opaque pointers.
| author | Paul Fisher <paul@pfish.zone> |
|---|---|
| date | Tue, 27 May 2025 14:37:28 -0400 |
| parents | bbe84835d6db |
| children | 8f3ae0c7ab92 |
comparison
equal
deleted
inserted
replaced
| 65:8e507c7af9cf | 66:a674799a5cd3 |
|---|---|
| 1 //! Where [PamHandle] lives. | 1 //! The wrapper types and traits for handles into the PAM library. |
| 2 use crate::constants::{ErrorCode, Result}; | |
| 2 use crate::items::{Item, ItemType}; | 3 use crate::items::{Item, ItemType}; |
| 3 use crate::{memory, pam_ffi, ErrorCode}; | 4 use crate::{memory, pam_ffi}; |
| 4 use libc::c_char; | 5 use libc::c_char; |
| 5 use secure_string::SecureString; | 6 use secure_string::SecureString; |
| 6 use std::ffi::{c_int, CString}; | 7 use std::ffi::{c_int, CString}; |
| 7 | 8 use std::mem; |
| 8 /// Your interface to a PAM handle. | 9 |
| 9 /// | 10 /// Features of a PAM handle that are available to applications and modules. |
| 10 /// This structure wraps an opaque PAM-provided pointer and gives you | 11 /// |
| 11 /// a safe and familiar struct-based API to interact with PAM. | 12 /// You probably want [`LibPamHandle`]. This trait is intended to allow creating |
| 12 #[repr(transparent)] | 13 /// mock PAM handle types used for testing PAM modules and applications. |
| 13 pub struct PamHandle(*mut libc::c_void); | 14 pub trait PamHandle { |
| 14 | |
| 15 impl PamHandle { | |
| 16 /// Retrieves the name of the user who is authenticating or logging in. | 15 /// Retrieves the name of the user who is authenticating or logging in. |
| 17 /// | 16 /// |
| 18 /// This is effectively like `handle.get_item::<Item::User>()`. | 17 /// This is effectively like `handle.get_item::<Item::User>()`. |
| 19 /// See the [`pam_get_user` manual page][man] | 18 /// See the [`pam_get_user` manual page][man] |
| 20 /// or [`pam_get_user` in the Module Writer's Guide][mwg]. | 19 /// or [`pam_get_user` in the Module Writer's Guide][mwg]. |
| 21 /// | 20 /// |
| 22 /// # Example | 21 /// # Example |
| 23 /// | 22 /// |
| 24 /// ```no_run | 23 /// ```no_run |
| 25 /// # use nonstick::PamHandle; | 24 /// # use nonstick::PamHandle; |
| 26 /// # fn _doc(handle: &PamHandle) -> Result<(), Box<dyn std::error::Error>> { | 25 /// # fn _doc(handle: &impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { |
| 27 /// // Get the username using the default prompt. | 26 /// // Get the username using the default prompt. |
| 28 /// let user = handle.get_user(None)?; | 27 /// let user = handle.get_user(None)?; |
| 29 /// // Get the username using a custom prompt. | 28 /// // Get the username using a custom prompt. |
| 30 /// let user = handle.get_user(Some("who ARE you even???"))?; | 29 /// let user = handle.get_user(Some("who ARE you even???"))?; |
| 31 /// # Ok(()) | 30 /// # Ok(()) |
| 32 /// # } | 31 /// # } |
| 33 /// ``` | 32 /// ``` |
| 34 /// | 33 /// |
| 35 /// [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 |
| 36 /// [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 |
| 37 pub fn get_user(&self, prompt: Option<&str>) -> crate::Result<String> { | 36 fn get_user(&self, prompt: Option<&str>) -> Result<String>; |
| 38 let prompt = memory::option_cstr(prompt)?; | |
| 39 let mut output: *const c_char = std::ptr::null_mut(); | |
| 40 let ret = unsafe { | |
| 41 pam_ffi::pam_get_user(self.0, &mut output, memory::prompt_ptr(prompt.as_ref())) | |
| 42 }; | |
| 43 ErrorCode::result_from(ret)?; | |
| 44 memory::copy_pam_string(output) | |
| 45 } | |
| 46 | 37 |
| 47 /// Retrieves the authentication token from the user. | 38 /// Retrieves the authentication token from the user. |
| 48 /// | 39 /// |
| 49 /// This is essentially like `handle.get_item::<Item::AuthTok>()`. | 40 /// This is essentially like `handle.get_item::<Item::AuthTok>()`. |
| 50 /// | 41 /// |
| 53 /// | 44 /// |
| 54 /// # Example | 45 /// # Example |
| 55 /// | 46 /// |
| 56 /// ```no_run | 47 /// ```no_run |
| 57 /// # use nonstick::PamHandle; | 48 /// # use nonstick::PamHandle; |
| 58 /// # fn _doc(handle: &PamHandle) -> Result<(), Box<dyn std::error::Error>> { | 49 /// # fn _doc(handle: &impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { |
| 59 /// // Get the user's password using the default prompt. | 50 /// // Get the user's password using the default prompt. |
| 60 /// let pass = handle.get_authtok(None)?; | 51 /// let pass = handle.get_authtok(None)?; |
| 61 /// // Get the user's password using a custom prompt. | 52 /// // Get the user's password using a custom prompt. |
| 62 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; | 53 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; |
| 63 /// Ok(()) | 54 /// Ok(()) |
| 64 /// # } | 55 /// # } |
| 65 /// ``` | 56 /// ``` |
| 66 /// | 57 /// |
| 67 /// [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 |
| 68 /// [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 |
| 69 pub fn get_authtok(&self, prompt: Option<&str>) -> crate::Result<SecureString> { | 60 fn get_authtok(&self, prompt: Option<&str>) -> Result<SecureString>; |
| 70 let prompt = memory::option_cstr(prompt)?; | |
| 71 let mut output: *const c_char = std::ptr::null_mut(); | |
| 72 let res = unsafe { | |
| 73 pam_ffi::pam_get_authtok( | |
| 74 self.0, | |
| 75 ItemType::AuthTok.into(), | |
| 76 &mut output, | |
| 77 memory::prompt_ptr(prompt.as_ref()), | |
| 78 ) | |
| 79 }; | |
| 80 ErrorCode::result_from(res)?; | |
| 81 memory::copy_pam_string(output).map(SecureString::from) | |
| 82 } | |
| 83 | 61 |
| 84 /// 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. |
| 85 /// | 63 /// |
| 86 /// These items are *references to PAM memory* | 64 /// These items are *references to PAM memory* |
| 87 /// which are *owned by the PAM session* | 65 /// which are *owned by the PAM session* |
| 94 /// | 72 /// |
| 95 /// ```no_run | 73 /// ```no_run |
| 96 /// # use nonstick::PamHandle; | 74 /// # use nonstick::PamHandle; |
| 97 /// use nonstick::items::Service; | 75 /// use nonstick::items::Service; |
| 98 /// | 76 /// |
| 99 /// # fn _doc(pam_handle: &PamHandle) -> Result<(), Box<dyn std::error::Error>> { | 77 /// # fn _doc(pam_handle: &impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { |
| 100 /// let svc: Option<Service> = pam_handle.get_item()?; | 78 /// let svc: Option<Service> = pam_handle.get_item()?; |
| 101 /// match svc { | 79 /// match svc { |
| 102 /// 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()), |
| 103 /// None => eprintln!("Who knows what the calling service is?"), | 81 /// None => eprintln!("Who knows what the calling service is?"), |
| 104 /// } | 82 /// } |
| 106 /// # } | 84 /// # } |
| 107 /// ``` | 85 /// ``` |
| 108 /// | 86 /// |
| 109 /// [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 |
| 110 /// [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 |
| 111 pub fn get_item<T: Item>(&self) -> crate::Result<Option<T>> { | 89 fn get_item<T: Item>(&self) -> Result<Option<T>>; |
| 90 | |
| 91 /// Sets an item in the PAM context. It can be retrieved using [`get_item`](Self::get_item). | |
| 92 /// | |
| 93 /// See the [`pam_set_item` manual page][man] | |
| 94 /// or [`pam_set_item` in the Module Writer's Guide][mwg]. | |
| 95 /// | |
| 96 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html | |
| 97 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item | |
| 98 fn set_item<T: Item>(&mut self, item: T) -> Result<()>; | |
| 99 | |
| 100 /// Closes the PAM session on an owned PAM handle. | |
| 101 /// | |
| 102 /// This should be called with the result of the application's last call | |
| 103 /// into PAM services. Since this is only applicable to *owned* PAM handles, | |
| 104 /// a PAM module should never call this (and it will never be handed | |
| 105 /// an owned `PamHandle` that it can `close`). | |
| 106 /// | |
| 107 /// See the [`pam_end` manual page][man] for more information. | |
| 108 /// | |
| 109 /// ```no_run | |
| 110 /// # use nonstick::PamHandle; | |
| 111 /// # use std::error::Error; | |
| 112 /// # fn _doc(handle: impl PamHandle, auth_result: nonstick::Result<()>) -> Result<(), Box<dyn Error>> { | |
| 113 /// // Earlier: authentication was performed and the result was stored | |
| 114 /// // into auth_result. | |
| 115 /// handle.close(auth_result)?; | |
| 116 /// # Ok(()) | |
| 117 /// # } | |
| 118 /// ``` | |
| 119 /// | |
| 120 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_end.3.html | |
| 121 fn close(self, status: Result<()>) -> Result<()>; | |
| 122 } | |
| 123 | |
| 124 /// Functionality of a PAM handle that can be expected by a PAM module. | |
| 125 /// | |
| 126 /// If you are not writing a PAM module (e.g., you are writing an application), | |
| 127 /// you should not use any of the functionality exposed by this trait. | |
| 128 /// | |
| 129 /// Like [`PamHandle`], this is intended to allow creating mock implementations | |
| 130 /// of PAM for testing PAM modules. | |
| 131 pub trait PamModuleHandle: PamHandle { | |
| 132 /// Gets some pointer, identified by `key`, that has been set previously | |
| 133 /// using [`set_data`](Self::set_data). | |
| 134 /// | |
| 135 /// The data, if present, is still owned by the current PAM session. | |
| 136 /// | |
| 137 /// See the [`pam_get_data` manual page][man] | |
| 138 /// or [`pam_get_data` in the Module Writer's Guide][mwg]. | |
| 139 /// | |
| 140 /// # Safety | |
| 141 /// | |
| 142 /// The data stored under the provided key must be of type `T`, | |
| 143 /// otherwise you'll get back a completely invalid `&T` | |
| 144 /// and further behavior is undefined. | |
| 145 /// | |
| 146 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html | |
| 147 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data | |
| 148 unsafe fn get_data<T>(&self, key: &str) -> Result<Option<&T>>; | |
| 149 | |
| 150 /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data). | |
| 151 /// | |
| 152 /// This data is accessible to this module and other PAM modules | |
| 153 /// (using the provided `key`), but is *not* accessible to the application. | |
| 154 /// The PAM session takes ownership of the data, and it will be dropped | |
| 155 /// when the session ends. | |
| 156 /// | |
| 157 /// See the [`pam_set_data` manual page][man] | |
| 158 /// or [`pam_set_data` in the Module Writer's Guide][mwg]. | |
| 159 /// | |
| 160 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html | |
| 161 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_data | |
| 162 fn set_data<T>(&mut self, key: &str, data: Box<T>) -> Result<()>; | |
| 163 } | |
| 164 | |
| 165 /// A [`PamHandle`] backed by `libpam`, i.e., a real PAM handle. | |
| 166 /// | |
| 167 /// This structure wraps an opaque PAM handle and gives you a nice Rusty | |
| 168 /// interface to use PAM. | |
| 169 #[repr(C)] | |
| 170 pub struct LibPamHandle(pam_ffi::Handle); | |
| 171 | |
| 172 impl LibPamHandle { | |
| 173 /// Converts a pointer passed from PAM into a borrowed handle. | |
| 174 /// | |
| 175 /// # Safety | |
| 176 /// | |
| 177 /// It is your responsibility to provide a valid pointer. | |
| 178 pub unsafe fn from_ptr<'a>(ptr: *mut libc::c_void) -> &'a mut LibPamHandle { | |
| 179 &mut *(ptr as *mut LibPamHandle) | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 impl Drop for LibPamHandle { | |
| 184 /// Ends the PAM session with a zero error code. | |
| 185 fn drop(&mut self) { | |
| 186 unsafe { | |
| 187 pam_ffi::pam_end(&mut self.0, 0); | |
| 188 } | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 impl PamHandle for LibPamHandle { | |
| 193 fn get_user(&self, prompt: Option<&str>) -> crate::Result<String> { | |
| 194 let prompt = memory::option_cstr(prompt)?; | |
| 195 let mut output: *const c_char = std::ptr::null_mut(); | |
| 196 let ret = unsafe { | |
| 197 pam_ffi::pam_get_user(&self.0, &mut output, memory::prompt_ptr(prompt.as_ref())) | |
| 198 }; | |
| 199 ErrorCode::result_from(ret)?; | |
| 200 memory::copy_pam_string(output) | |
| 201 } | |
| 202 | |
| 203 fn get_authtok(&self, prompt: Option<&str>) -> crate::Result<SecureString> { | |
| 204 let prompt = memory::option_cstr(prompt)?; | |
| 205 let mut output: *const c_char = std::ptr::null_mut(); | |
| 206 let res = unsafe { | |
| 207 pam_ffi::pam_get_authtok( | |
| 208 &self.0, | |
| 209 ItemType::AuthTok.into(), | |
| 210 &mut output, | |
| 211 memory::prompt_ptr(prompt.as_ref()), | |
| 212 ) | |
| 213 }; | |
| 214 ErrorCode::result_from(res)?; | |
| 215 memory::copy_pam_string(output).map(SecureString::from) | |
| 216 } | |
| 217 | |
| 218 fn get_item<T: Item>(&self) -> crate::Result<Option<T>> { | |
| 112 let mut ptr: *const libc::c_void = std::ptr::null(); | 219 let mut ptr: *const libc::c_void = std::ptr::null(); |
| 113 let out = unsafe { | 220 let out = unsafe { |
| 114 let ret = pam_ffi::pam_get_item(self.0, T::type_id().into(), &mut ptr); | 221 let ret = pam_ffi::pam_get_item(&self.0, T::type_id().into(), &mut ptr); |
| 115 ErrorCode::result_from(ret)?; | 222 ErrorCode::result_from(ret)?; |
| 116 let typed_ptr: *const T::Raw = ptr.cast(); | 223 let typed_ptr: *const T::Raw = ptr.cast(); |
| 117 match typed_ptr.is_null() { | 224 match typed_ptr.is_null() { |
| 118 true => None, | 225 true => None, |
| 119 false => Some(T::from_raw(typed_ptr)), | 226 false => Some(T::from_raw(typed_ptr)), |
| 120 } | 227 } |
| 121 }; | 228 }; |
| 122 Ok(out) | 229 Ok(out) |
| 123 } | 230 } |
| 124 | 231 |
| 125 /// Sets an item in the PAM context. It can be retrieved using [`get_item`](Self::get_item). | 232 fn set_item<T: Item>(&mut self, item: T) -> crate::Result<()> { |
| 126 /// | 233 let ret = unsafe { |
| 127 /// See the [`pam_set_item` manual page][man] | 234 pam_ffi::pam_set_item(&mut self.0, T::type_id().into(), item.into_raw().cast()) |
| 128 /// or [`pam_set_item` in the Module Writer's Guide][mwg]. | 235 }; |
| 129 /// | |
| 130 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html | |
| 131 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item | |
| 132 pub fn set_item<T: Item>(&mut self, item: T) -> crate::Result<()> { | |
| 133 let ret = | |
| 134 unsafe { pam_ffi::pam_set_item(self.0, T::type_id().into(), item.into_raw().cast()) }; | |
| 135 ErrorCode::result_from(ret) | 236 ErrorCode::result_from(ret) |
| 136 } | 237 } |
| 137 | 238 |
| 138 /// Gets some pointer, identified by `key`, that has been set previously | 239 fn close(mut self, status: Result<()>) -> Result<()> { |
| 139 /// using [`set_data`](Self::set_data). | 240 let result = unsafe { pam_ffi::pam_end(&mut self.0, ErrorCode::result_to_c(status)) }; |
| 140 /// | 241 // Since we've already `pam_end`ed this session, we don't want it to be |
| 141 /// The data, if present, is still owned by the current PAM session. | 242 // double-freed on drop. |
| 142 /// | 243 mem::forget(self); |
| 143 /// See the [`pam_get_data` manual page][man] | 244 ErrorCode::result_from(result) |
| 144 /// or [`pam_get_data` in the Module Writer's Guide][mwg]. | 245 } |
| 145 /// | 246 } |
| 146 /// # Safety | 247 |
| 147 /// | 248 impl PamModuleHandle for LibPamHandle { |
| 148 /// The data stored under the provided key must be of type `T`, | 249 unsafe fn get_data<T>(&self, key: &str) -> crate::Result<Option<&T>> { |
| 149 /// otherwise you'll get back a completely invalid `&T` | |
| 150 /// and further behavior is undefined. | |
| 151 /// | |
| 152 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html | |
| 153 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data | |
| 154 pub unsafe fn get_data<T>(&self, key: &str) -> crate::Result<Option<&T>> { | |
| 155 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; | 250 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; |
| 156 let mut ptr: *const libc::c_void = std::ptr::null(); | 251 let mut ptr: *const libc::c_void = std::ptr::null(); |
| 157 ErrorCode::result_from(pam_ffi::pam_get_data(self.0, c_key.as_ptr(), &mut ptr))?; | 252 ErrorCode::result_from(pam_ffi::pam_get_data(&self.0, c_key.as_ptr(), &mut ptr))?; |
| 158 match ptr.is_null() { | 253 match ptr.is_null() { |
| 159 true => Ok(None), | 254 true => Ok(None), |
| 160 false => { | 255 false => { |
| 161 let typed_ptr = ptr.cast(); | 256 let typed_ptr = ptr.cast(); |
| 162 Ok(Some(&*typed_ptr)) | 257 Ok(Some(&*typed_ptr)) |
| 163 } | 258 } |
| 164 } | 259 } |
| 165 } | 260 } |
| 166 | 261 |
| 167 /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data). | 262 fn set_data<T>(&mut self, key: &str, data: Box<T>) -> crate::Result<()> { |
| 168 /// | |
| 169 /// This data is accessible to this module and other PAM modules | |
| 170 /// (using the provided `key`), but is *not* accessible to the application. | |
| 171 /// The PAM session takes ownership of the data, and it will be dropped | |
| 172 /// when the session ends. | |
| 173 /// | |
| 174 /// See the [`pam_set_data` manual page][man] | |
| 175 /// or [`pam_set_data` in the Module Writer's Guide][mwg]. | |
| 176 /// | |
| 177 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html | |
| 178 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_data | |
| 179 pub fn set_data<T>(&mut self, key: &str, data: Box<T>) -> crate::Result<()> { | |
| 180 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; | 263 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; |
| 181 let ret = unsafe { | 264 let ret = unsafe { |
| 182 pam_ffi::pam_set_data( | 265 pam_ffi::pam_set_data( |
| 183 self.0, | 266 &mut self.0, |
| 184 c_key.as_ptr(), | 267 c_key.as_ptr(), |
| 185 Box::into_raw(data).cast(), | 268 Box::into_raw(data).cast(), |
| 186 Self::set_data_cleanup::<T>, | 269 set_data_cleanup::<T>, |
| 187 ) | 270 ) |
| 188 }; | 271 }; |
| 189 ErrorCode::result_from(ret) | 272 ErrorCode::result_from(ret) |
| 190 } | 273 } |
| 191 | 274 } |
| 192 /// Function called at the end of a PAM session that is called to clean up | 275 |
| 193 /// a value previously provided to PAM in a `pam_set_data` call. | 276 /// Function called at the end of a PAM session that is called to clean up |
| 194 /// | 277 /// a value previously provided to PAM in a `pam_set_data` call. |
| 195 /// You should never call this yourself. | 278 /// |
| 196 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) { | 279 /// You should never call this yourself. |
| 197 unsafe { | 280 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) { |
| 198 let _data: Box<T> = Box::from_raw(c_data.cast()); | 281 unsafe { |
| 199 } | 282 let _data: Box<T> = Box::from_raw(c_data.cast()); |
| 200 } | 283 } |
| 201 } | 284 } |
| 202 | |
| 203 impl From<*mut libc::c_void> for PamHandle { | |
| 204 /// Wraps an internal Handle pointer. | |
| 205 fn from(value: *mut libc::c_void) -> Self { | |
| 206 Self(value) | |
| 207 } | |
| 208 } |
