Mercurial > crates > nonstick
comparison src/libpam/handle.rs @ 130:80c07e5ab22f
Transfer over (almost) completely to using libpam-sys.
This reimplements everything in nonstick on top of the new -sys crate.
We don't yet use libpam-sys's helpers for binary message payloads. Soon.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 01 Jul 2025 06:11:43 -0400 |
parents | 39760dfc9b3b |
children | a632a8874131 |
comparison
equal
deleted
inserted
replaced
129:5b2de52dd8b2 | 130:80c07e5ab22f |
---|---|
1 use super::conversation::LibPamConversation; | 1 use super::conversation::LibPamConversation; |
2 use crate::constants::{ErrorCode, Result}; | 2 use crate::constants::{ErrorCode, Result}; |
3 use crate::conv::Message; | 3 use crate::conv::Exchange; |
4 use crate::environ::EnvironMapMut; | 4 use crate::environ::EnvironMapMut; |
5 use crate::handle::PamShared; | 5 use crate::handle::PamShared; |
6 use crate::libpam::environ::{LibPamEnviron, LibPamEnvironMut}; | 6 use crate::libpam::environ::{LibPamEnviron, LibPamEnvironMut}; |
7 pub use crate::libpam::pam_ffi::LibPamHandle; | 7 use crate::libpam::memory; |
8 use crate::libpam::{memory, pam_ffi}; | |
9 use crate::logging::{Level, Location}; | 8 use crate::logging::{Level, Location}; |
10 use crate::{ | 9 use crate::{ |
11 guide, linklist, stdlinks, Conversation, EnvironMap, Flags, PamHandleApplication, | 10 guide, linklist, stdlinks, Conversation, EnvironMap, Flags, PamHandleApplication, |
12 PamHandleModule, | 11 PamHandleModule, |
13 }; | 12 }; |
14 use num_enum::{IntoPrimitive, TryFromPrimitive}; | 13 use num_enum::{IntoPrimitive, TryFromPrimitive}; |
15 use std::cell::Cell; | 14 use std::cell::Cell; |
16 use std::ffi::{c_char, c_int, CString}; | 15 use std::ffi::{c_char, c_int, CString}; |
17 use std::ops::{Deref, DerefMut}; | 16 |
18 use std::ptr; | 17 use std::ptr; |
18 use std::ptr::NonNull; | |
19 use libpam_sys::cfg_pam_impl; | |
19 | 20 |
20 /// Owner for a PAM handle. | 21 /// Owner for a PAM handle. |
21 struct HandleWrap(*mut LibPamHandle); | 22 pub struct LibPamHandle(pub NonNull<libpam_sys::pam_handle>); |
22 | 23 |
23 impl Deref for HandleWrap { | 24 impl AsRef<libpam_sys::pam_handle> for LibPamHandle { |
24 type Target = LibPamHandle; | 25 fn as_ref(&self) -> &libpam_sys::pam_handle { |
25 fn deref(&self) -> &Self::Target { | 26 unsafe { self.0.as_ref() } |
26 unsafe { &*self.0 } | 27 } |
27 } | 28 } |
28 } | 29 |
29 | 30 impl AsMut<libpam_sys::pam_handle> for LibPamHandle { |
30 impl DerefMut for HandleWrap { | 31 fn as_mut(&mut self) -> &mut libpam_sys::pam_handle { |
31 fn deref_mut(&mut self) -> &mut Self::Target { | 32 unsafe { self.0.as_mut() } |
32 unsafe { &mut *self.0 } | |
33 } | 33 } |
34 } | 34 } |
35 | 35 |
36 /// An owned PAM handle. | 36 /// An owned PAM handle. |
37 pub struct OwnedLibPamHandle<'a> { | 37 pub struct OwnedLibPamHandle<'a> { |
38 /// The handle itself. | 38 /// The handle itself. |
39 handle: HandleWrap, | 39 handle: LibPamHandle, |
40 /// The last return value from the handle. | 40 /// The last return value from the handle. |
41 last_return: Cell<Result<()>>, | 41 last_return: Cell<Result<()>>, |
42 /// If set, the Conversation that this PAM handle owns. | 42 /// If set, the Conversation that this PAM handle owns. |
43 /// | 43 /// |
44 /// We have to hold on to this because the PAM specification doesn't | 44 /// We have to hold on to this because the PAM specification doesn't |
100 ) -> Result<Self> { | 100 ) -> Result<Self> { |
101 let conv = Box::new(LibPamConversation::wrap(conversation)); | 101 let conv = Box::new(LibPamConversation::wrap(conversation)); |
102 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?; | 102 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?; |
103 let username_cstr = memory::prompt_ptr(memory::option_cstr(username.as_deref())?.as_ref()); | 103 let username_cstr = memory::prompt_ptr(memory::option_cstr(username.as_deref())?.as_ref()); |
104 | 104 |
105 let mut handle: *mut LibPamHandle = ptr::null_mut(); | 105 let mut handle: *mut libpam_sys::pam_handle = ptr::null_mut(); |
106 // SAFETY: We've set everything up properly to call `pam_start`. | 106 // SAFETY: We've set everything up properly to call `pam_start`. |
107 // The returned value will be a valid pointer provided the result is OK. | 107 // The returned value will be a valid pointer provided the result is OK. |
108 let result = unsafe { | 108 let result = unsafe { |
109 pam_ffi::pam_start( | 109 libpam_sys::pam_start( |
110 service_cstr.as_ptr(), | 110 service_cstr.as_ptr(), |
111 username_cstr, | 111 username_cstr, |
112 conv.as_ref(), | 112 (conv.as_ref() as *const LibPamConversation) |
113 .cast_mut() | |
114 .cast(), | |
113 &mut handle, | 115 &mut handle, |
114 ) | 116 ) |
115 }; | 117 }; |
116 ErrorCode::result_from(result)?; | 118 ErrorCode::result_from(result)?; |
119 let handle = NonNull::new(handle).ok_or(ErrorCode::BufferError)?; | |
117 Ok(Self { | 120 Ok(Self { |
118 handle: HandleWrap(handle), | 121 handle: LibPamHandle(handle), |
119 last_return: Cell::new(Ok(())), | 122 last_return: Cell::new(Ok(())), |
120 conversation: Some(conv), | 123 conversation: Some(conv), |
121 }) | 124 }) |
122 } | 125 } |
123 } | 126 } |
124 | 127 |
125 impl PamHandleApplication for OwnedLibPamHandle<'_> { | 128 impl PamHandleApplication for OwnedLibPamHandle<'_> { |
126 fn authenticate(&mut self, flags: Flags) -> Result<()> { | 129 fn authenticate(&mut self, flags: Flags) -> Result<()> { |
127 let ret = unsafe { pam_ffi::pam_authenticate(self.handle.0, flags.bits() as c_int) }; | 130 let ret = |
131 unsafe { libpam_sys::pam_authenticate(self.handle.as_mut(), flags.bits() as c_int) }; | |
128 let result = ErrorCode::result_from(ret); | 132 let result = ErrorCode::result_from(ret); |
129 self.last_return.set(result); | 133 self.last_return.set(result); |
130 result | 134 result |
131 } | 135 } |
132 | 136 |
133 fn account_management(&mut self, flags: Flags) -> Result<()> { | 137 fn account_management(&mut self, flags: Flags) -> Result<()> { |
134 let ret = unsafe { pam_ffi::pam_acct_mgmt(self.handle.0, flags.bits() as c_int) }; | 138 let ret = unsafe { libpam_sys::pam_acct_mgmt(self.handle.as_mut(), flags.bits() as c_int) }; |
135 let result = ErrorCode::result_from(ret); | 139 let result = ErrorCode::result_from(ret); |
136 self.last_return.set(result); | 140 self.last_return.set(result); |
137 result | 141 result |
138 } | 142 } |
139 | 143 |
140 fn change_authtok(&mut self, flags: Flags) -> Result<()> { | 144 fn change_authtok(&mut self, flags: Flags) -> Result<()> { |
141 let ret = unsafe { pam_ffi::pam_chauthtok(self.handle.0, flags.bits() as c_int) }; | 145 let ret = unsafe { libpam_sys::pam_chauthtok(self.handle.as_mut(), flags.bits() as c_int) }; |
142 let result = ErrorCode::result_from(ret); | 146 let result = ErrorCode::result_from(ret); |
143 self.last_return.set(result); | 147 self.last_return.set(result); |
144 result | 148 result |
145 } | 149 } |
146 } | 150 } |
165 /// | 169 /// |
166 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] | 170 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] |
167 #[doc = stdlinks!(3 pam_end)] | 171 #[doc = stdlinks!(3 pam_end)] |
168 fn drop(&mut self) { | 172 fn drop(&mut self) { |
169 unsafe { | 173 unsafe { |
170 pam_ffi::pam_end( | 174 libpam_sys::pam_end( |
171 self.handle.0, | 175 self.handle.as_mut(), |
172 ErrorCode::result_to_c(self.last_return.get()), | 176 ErrorCode::result_to_c(self.last_return.get()), |
173 ); | 177 ); |
174 } | 178 } |
175 } | 179 } |
176 } | 180 } |
188 } | 192 } |
189 }; | 193 }; |
190 } | 194 } |
191 | 195 |
192 impl PamShared for LibPamHandle { | 196 impl PamShared for LibPamHandle { |
197 #[cfg(any())] | |
193 fn log(&self, level: Level, loc: Location<'_>, entry: &str) { | 198 fn log(&self, level: Level, loc: Location<'_>, entry: &str) { |
194 let entry = match CString::new(entry).or_else(|_| CString::new(dbg!(entry))) { | 199 let entry = match CString::new(entry).or_else(|_| CString::new(dbg!(entry))) { |
195 Ok(cstr) => cstr, | 200 Ok(cstr) => cstr, |
196 _ => return, | 201 _ => return, |
197 }; | 202 }; |
198 #[cfg(pam_impl = "linux-pam")] | 203 #[cfg(pam_impl = "linux-pam")] |
199 { | 204 { |
200 _ = loc; | 205 _ = loc; |
201 // SAFETY: We're calling this function with a known value. | 206 // SAFETY: We're calling this function with a known value. |
202 unsafe { | 207 unsafe { |
203 pam_ffi::pam_syslog(self, level as c_int, "%s\0".as_ptr().cast(), entry.as_ptr()) | 208 libpam_sys::pam_syslog(self, level as c_int, "%s\0".as_ptr().cast(), entry.as_ptr()) |
204 } | 209 } |
205 } | 210 } |
206 #[cfg(pam_impl = "openpam")] | 211 #[cfg(pam_impl = "openpam")] |
207 { | 212 { |
208 let func = CString::new(loc.function).unwrap_or(CString::default()); | 213 let func = CString::new(loc.function).unwrap_or(CString::default()); |
209 // SAFETY: We're calling this function with a known value. | 214 // SAFETY: We're calling this function with a known value. |
210 unsafe { | 215 unsafe { |
211 pam_ffi::_openpam_log( | 216 libpam_sys::_openpam_log( |
212 level as c_int, | 217 level as c_int, |
213 func.as_ptr(), | 218 func.as_ptr(), |
214 "%s\0".as_ptr().cast(), | 219 "%s\0".as_ptr().cast(), |
215 entry.as_ptr(), | 220 entry.as_ptr(), |
216 ) | 221 ) |
217 } | 222 } |
218 } | 223 } |
219 } | 224 } |
220 | 225 |
226 fn log(&self, _level: Level, _loc: Location<'_>, _entry: &str) {} | |
227 | |
221 fn username(&mut self, prompt: Option<&str>) -> Result<String> { | 228 fn username(&mut self, prompt: Option<&str>) -> Result<String> { |
222 let prompt = memory::option_cstr(prompt)?; | 229 let prompt = memory::option_cstr(prompt)?; |
223 let mut output: *const c_char = ptr::null(); | 230 let mut output: *const c_char = ptr::null(); |
224 let ret = unsafe { | 231 let ret = unsafe { |
225 pam_ffi::pam_get_user(self, &mut output, memory::prompt_ptr(prompt.as_ref())) | 232 libpam_sys::pam_get_user( |
233 self.as_mut(), | |
234 &mut output, | |
235 memory::prompt_ptr(prompt.as_ref()), | |
236 ) | |
226 }; | 237 }; |
227 ErrorCode::result_from(ret)?; | 238 ErrorCode::result_from(ret)?; |
228 unsafe { memory::copy_pam_string(output) } | 239 unsafe { memory::copy_pam_string(output) } |
229 .transpose() | 240 .transpose() |
230 .unwrap_or(Err(ErrorCode::ConversationError)) | 241 .unwrap_or(Err(ErrorCode::ConversationError)) |
253 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); | 264 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); |
254 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); | 265 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); |
255 } | 266 } |
256 | 267 |
257 impl Conversation for LibPamHandle { | 268 impl Conversation for LibPamHandle { |
258 fn communicate(&self, messages: &[Message]) { | 269 fn communicate(&self, messages: &[Exchange]) { |
259 match self.conversation_item() { | 270 match self.conversation_item() { |
260 Ok(conv) => conv.communicate(messages), | 271 Ok(conv) => conv.communicate(messages), |
261 Err(e) => { | 272 Err(e) => { |
262 for msg in messages { | 273 for msg in messages { |
263 msg.set_error(e) | 274 msg.set_error(e) |
266 } | 277 } |
267 } | 278 } |
268 } | 279 } |
269 | 280 |
270 impl PamHandleModule for LibPamHandle { | 281 impl PamHandleModule for LibPamHandle { |
282 #[cfg_pam_impl(any("LinuxPam", "OpenPam"))] | |
271 fn authtok(&mut self, prompt: Option<&str>) -> Result<String> { | 283 fn authtok(&mut self, prompt: Option<&str>) -> Result<String> { |
272 let prompt = memory::option_cstr(prompt)?; | 284 let prompt = memory::option_cstr(prompt)?; |
273 let mut output: *const c_char = ptr::null_mut(); | 285 let mut output: *const c_char = ptr::null_mut(); |
274 // SAFETY: We're calling this with known-good values. | 286 // SAFETY: We're calling this with known-good values. |
275 let res = unsafe { | 287 let res = unsafe { |
276 pam_ffi::pam_get_authtok( | 288 libpam_sys::pam_get_authtok( |
277 self, | 289 self.as_mut(), |
278 ItemType::AuthTok.into(), | 290 ItemType::AuthTok.into(), |
279 &mut output, | 291 &mut output, |
280 memory::prompt_ptr(prompt.as_ref()), | 292 memory::prompt_ptr(prompt.as_ref()), |
281 ) | 293 ) |
282 }; | 294 }; |
283 ErrorCode::result_from(res)?; | 295 ErrorCode::result_from(res)?; |
284 // SAFETY: We got this string from PAM. | 296 // SAFETY: We got this string from PAM. |
285 unsafe { memory::copy_pam_string(output) } | 297 unsafe { memory::copy_pam_string(output) } |
286 .transpose() | 298 .transpose() |
287 .unwrap_or(Err(ErrorCode::ConversationError)) | 299 .unwrap_or(Err(ErrorCode::ConversationError)) |
300 } | |
301 | |
302 #[cfg_pam_impl(not(any("LinuxPam", "OpenPam")))] | |
303 fn authtok(&mut self, prompt: Option<&str>) -> Result<String> { | |
304 Err(ErrorCode::ConversationError) | |
288 } | 305 } |
289 | 306 |
290 cstr_item!(get = authtok_item, item = ItemType::AuthTok); | 307 cstr_item!(get = authtok_item, item = ItemType::AuthTok); |
291 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok); | 308 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok); |
292 } | 309 } |
307 /// # Safety | 324 /// # Safety |
308 /// | 325 /// |
309 /// You better be requesting an item which is a C string. | 326 /// You better be requesting an item which is a C string. |
310 unsafe fn get_cstr_item(&self, item_type: ItemType) -> Result<Option<String>> { | 327 unsafe fn get_cstr_item(&self, item_type: ItemType) -> Result<Option<String>> { |
311 let mut output = ptr::null(); | 328 let mut output = ptr::null(); |
312 let ret = unsafe { pam_ffi::pam_get_item(self, item_type as c_int, &mut output) }; | 329 let ret = |
330 unsafe { libpam_sys::pam_get_item(self.as_ref(), item_type as c_int, &mut output) }; | |
313 ErrorCode::result_from(ret)?; | 331 ErrorCode::result_from(ret)?; |
314 memory::copy_pam_string(output.cast()) | 332 memory::copy_pam_string(output.cast()) |
315 } | 333 } |
316 | 334 |
317 /// Sets a C string item. | 335 /// Sets a C string item. |
320 /// | 338 /// |
321 /// You better be setting an item which is a C string. | 339 /// You better be setting an item which is a C string. |
322 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> { | 340 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> { |
323 let data_str = memory::option_cstr(data)?; | 341 let data_str = memory::option_cstr(data)?; |
324 let ret = unsafe { | 342 let ret = unsafe { |
325 pam_ffi::pam_set_item( | 343 libpam_sys::pam_set_item( |
326 self, | 344 self.as_mut(), |
327 item_type as c_int, | 345 item_type as c_int, |
328 memory::prompt_ptr(data_str.as_ref()).cast(), | 346 memory::prompt_ptr(data_str.as_ref()).cast(), |
329 ) | 347 ) |
330 }; | 348 }; |
331 ErrorCode::result_from(ret) | 349 ErrorCode::result_from(ret) |
333 | 351 |
334 /// Gets the `PAM_CONV` item from the handle. | 352 /// Gets the `PAM_CONV` item from the handle. |
335 fn conversation_item(&self) -> Result<&mut LibPamConversation<'_>> { | 353 fn conversation_item(&self) -> Result<&mut LibPamConversation<'_>> { |
336 let output: *mut LibPamConversation = ptr::null_mut(); | 354 let output: *mut LibPamConversation = ptr::null_mut(); |
337 let result = unsafe { | 355 let result = unsafe { |
338 pam_ffi::pam_get_item( | 356 libpam_sys::pam_get_item( |
339 self, | 357 self.as_ref(), |
340 ItemType::Conversation.into(), | 358 ItemType::Conversation.into(), |
341 &mut output.cast_const().cast(), | 359 &mut output.cast_const().cast(), |
342 ) | 360 ) |
343 }; | 361 }; |
344 ErrorCode::result_from(result)?; | 362 ErrorCode::result_from(result)?; |