Mercurial > crates > nonstick
comparison src/libpam/handle.rs @ 75:c30811b4afae
rename pam_ffi submodule to libpam.
| author | Paul Fisher <paul@pfish.zone> |
|---|---|
| date | Fri, 06 Jun 2025 22:35:08 -0400 |
| parents | src/pam_ffi/handle.rs@c7c596e6388f |
| children | 351bdc13005e |
comparison
equal
deleted
inserted
replaced
| 74:c7c596e6388f | 75:c30811b4afae |
|---|---|
| 1 use super::conversation::LibPamConversation; | |
| 2 use crate::constants::{ErrorCode, InvalidEnum, Result}; | |
| 3 use crate::conv::Message; | |
| 4 use crate::handle::{PamApplicationOnly, PamModuleOnly, PamShared}; | |
| 5 use crate::libpam::memory; | |
| 6 use crate::libpam::memory::Immovable; | |
| 7 use crate::{Conversation, Response}; | |
| 8 use num_derive::FromPrimitive; | |
| 9 use num_traits::FromPrimitive; | |
| 10 use std::ffi::{c_char, c_int}; | |
| 11 use std::ops::{Deref, DerefMut}; | |
| 12 use std::result::Result as StdResult; | |
| 13 use std::{mem, ptr}; | |
| 14 | |
| 15 /// An owned PAM handle. | |
| 16 #[repr(transparent)] | |
| 17 pub struct OwnedLibPamHandle(*mut LibPamHandle); | |
| 18 | |
| 19 /// An opaque structure that a PAM handle points to. | |
| 20 #[repr(C)] | |
| 21 pub struct LibPamHandle { | |
| 22 _data: (), | |
| 23 _marker: Immovable, | |
| 24 } | |
| 25 | |
| 26 impl LibPamHandle { | |
| 27 /// Gets a C string item. | |
| 28 /// | |
| 29 /// # Safety | |
| 30 /// | |
| 31 /// You better be requesting an item which is a C string. | |
| 32 unsafe fn get_cstr_item(&mut self, item_type: ItemType) -> Result<Option<&str>> { | |
| 33 let mut output = ptr::null(); | |
| 34 let ret = unsafe { super::pam_get_item(self, item_type as c_int, &mut output) }; | |
| 35 ErrorCode::result_from(ret)?; | |
| 36 memory::wrap_string(output.cast()) | |
| 37 } | |
| 38 | |
| 39 /// Sets a C string item. | |
| 40 /// | |
| 41 /// # Safety | |
| 42 /// | |
| 43 /// You better be setting an item which is a C string. | |
| 44 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> { | |
| 45 let data_str = memory::option_cstr(data)?; | |
| 46 let ret = unsafe { | |
| 47 super::pam_set_item( | |
| 48 self, | |
| 49 item_type as c_int, | |
| 50 memory::prompt_ptr(data_str.as_ref()).cast(), | |
| 51 ) | |
| 52 }; | |
| 53 ErrorCode::result_from(ret) | |
| 54 } | |
| 55 | |
| 56 /// Gets the `PAM_CONV` item from the handle. | |
| 57 fn conversation_item(&mut self) -> Result<&mut LibPamConversation> { | |
| 58 let output: *mut LibPamConversation = ptr::null_mut(); | |
| 59 let result = unsafe { | |
| 60 super::pam_get_item( | |
| 61 self, | |
| 62 ItemType::Conversation.into(), | |
| 63 &mut output.cast_const().cast(), | |
| 64 ) | |
| 65 }; | |
| 66 ErrorCode::result_from(result)?; | |
| 67 // SAFETY: We got this result from PAM, and we're checking if it's null. | |
| 68 unsafe { output.as_mut() }.ok_or(ErrorCode::ConversationError) | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 impl PamApplicationOnly for OwnedLibPamHandle { | |
| 73 fn close(self, status: Result<()>) -> Result<()> { | |
| 74 let ret = unsafe { super::pam_end(self.0, ErrorCode::result_to_c(status)) }; | |
| 75 // Forget rather than dropping, since dropping also calls pam_end. | |
| 76 mem::forget(self); | |
| 77 ErrorCode::result_from(ret) | |
| 78 } | |
| 79 } | |
| 80 | |
| 81 impl Deref for OwnedLibPamHandle { | |
| 82 type Target = LibPamHandle; | |
| 83 fn deref(&self) -> &Self::Target { | |
| 84 unsafe { &*self.0 } | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 impl DerefMut for OwnedLibPamHandle { | |
| 89 fn deref_mut(&mut self) -> &mut Self::Target { | |
| 90 unsafe { &mut *self.0 } | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 impl Drop for OwnedLibPamHandle { | |
| 95 /// Ends the PAM session with a zero error code. | |
| 96 /// You probably want to call [`close`](Self::close) instead of | |
| 97 /// letting this drop by itself. | |
| 98 fn drop(&mut self) { | |
| 99 unsafe { | |
| 100 super::pam_end(self.0, 0); | |
| 101 } | |
| 102 } | |
| 103 } | |
| 104 | |
| 105 macro_rules! cstr_item { | |
| 106 (get = $getter:ident, item = $item_type:path) => { | |
| 107 fn $getter(&mut self) -> Result<Option<&str>> { | |
| 108 unsafe { self.get_cstr_item($item_type) } | |
| 109 } | |
| 110 }; | |
| 111 (set = $setter:ident, item = $item_type:path) => { | |
| 112 fn $setter(&mut self, value: Option<&str>) -> Result<()> { | |
| 113 unsafe { self.set_cstr_item($item_type, value) } | |
| 114 } | |
| 115 }; | |
| 116 } | |
| 117 | |
| 118 impl PamShared for LibPamHandle { | |
| 119 fn get_user(&mut self, prompt: Option<&str>) -> Result<&str> { | |
| 120 let prompt = memory::option_cstr(prompt)?; | |
| 121 let mut output: *const c_char = ptr::null(); | |
| 122 let ret = | |
| 123 unsafe { super::pam_get_user(self, &mut output, memory::prompt_ptr(prompt.as_ref())) }; | |
| 124 ErrorCode::result_from(ret)?; | |
| 125 unsafe { memory::wrap_string(output) } | |
| 126 .transpose() | |
| 127 .unwrap_or(Err(ErrorCode::ConversationError)) | |
| 128 } | |
| 129 | |
| 130 cstr_item!(get = user_item, item = ItemType::User); | |
| 131 cstr_item!(set = set_user_item, item = ItemType::User); | |
| 132 cstr_item!(get = service, item = ItemType::Service); | |
| 133 cstr_item!(set = set_service, item = ItemType::Service); | |
| 134 cstr_item!(get = user_prompt, item = ItemType::UserPrompt); | |
| 135 cstr_item!(set = set_user_prompt, item = ItemType::UserPrompt); | |
| 136 cstr_item!(get = tty_name, item = ItemType::Tty); | |
| 137 cstr_item!(set = set_tty_name, item = ItemType::Tty); | |
| 138 cstr_item!(get = remote_user, item = ItemType::RemoteUser); | |
| 139 cstr_item!(set = set_remote_user, item = ItemType::RemoteUser); | |
| 140 cstr_item!(get = remote_host, item = ItemType::RemoteHost); | |
| 141 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost); | |
| 142 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); | |
| 143 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); | |
| 144 } | |
| 145 | |
| 146 impl Conversation for LibPamHandle { | |
| 147 fn communicate(&mut self, messages: &[Message]) -> Result<Vec<Response>> { | |
| 148 self.conversation_item()?.communicate(messages) | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 impl PamModuleOnly for LibPamHandle { | |
| 153 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<&str> { | |
| 154 let prompt = memory::option_cstr(prompt)?; | |
| 155 let mut output: *const c_char = ptr::null_mut(); | |
| 156 // SAFETY: We're calling this with known-good values. | |
| 157 let res = unsafe { | |
| 158 super::pam_get_authtok( | |
| 159 self, | |
| 160 ItemType::AuthTok.into(), | |
| 161 &mut output, | |
| 162 memory::prompt_ptr(prompt.as_ref()), | |
| 163 ) | |
| 164 }; | |
| 165 ErrorCode::result_from(res)?; | |
| 166 // SAFETY: We got this string from PAM. | |
| 167 unsafe { memory::wrap_string(output) } | |
| 168 .transpose() | |
| 169 .unwrap_or(Err(ErrorCode::ConversationError)) | |
| 170 } | |
| 171 | |
| 172 cstr_item!(get = authtok_item, item = ItemType::AuthTok); | |
| 173 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok); | |
| 174 } | |
| 175 | |
| 176 /// Function called at the end of a PAM session that is called to clean up | |
| 177 /// a value previously provided to PAM in a `pam_set_data` call. | |
| 178 /// | |
| 179 /// You should never call this yourself. | |
| 180 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) { | |
| 181 unsafe { | |
| 182 let _data: Box<T> = Box::from_raw(c_data.cast()); | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 /// Identifies what is being gotten or set with `pam_get_item` | |
| 187 /// or `pam_set_item`. | |
| 188 #[derive(FromPrimitive)] | |
| 189 #[repr(i32)] | |
| 190 #[non_exhaustive] // because C could give us anything! | |
| 191 pub enum ItemType { | |
| 192 /// The PAM service name. | |
| 193 Service = 1, | |
| 194 /// The user's login name. | |
| 195 User = 2, | |
| 196 /// The TTY name. | |
| 197 Tty = 3, | |
| 198 /// The remote host (if applicable). | |
| 199 RemoteHost = 4, | |
| 200 /// The conversation struct (not a CStr-based item). | |
| 201 Conversation = 5, | |
| 202 /// The authentication token (password). | |
| 203 AuthTok = 6, | |
| 204 /// The old authentication token (when changing passwords). | |
| 205 OldAuthTok = 7, | |
| 206 /// The remote user's name. | |
| 207 RemoteUser = 8, | |
| 208 /// The prompt shown when requesting a username. | |
| 209 UserPrompt = 9, | |
| 210 /// App-supplied function to override failure delays. | |
| 211 FailDelay = 10, | |
| 212 /// X display name. | |
| 213 XDisplay = 11, | |
| 214 /// X server authentication data. | |
| 215 XAuthData = 12, | |
| 216 /// The type of `pam_get_authtok`. | |
| 217 AuthTokType = 13, | |
| 218 } | |
| 219 | |
| 220 impl TryFrom<c_int> for ItemType { | |
| 221 type Error = InvalidEnum<Self>; | |
| 222 fn try_from(value: c_int) -> StdResult<Self, Self::Error> { | |
| 223 Self::from_i32(value).ok_or(value.into()) | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 impl From<ItemType> for c_int { | |
| 228 fn from(val: ItemType) -> Self { | |
| 229 val as Self | |
| 230 } | |
| 231 } |
