comparison src/libpam/handle.rs @ 101:94b51fa4f797

Fix memory soundness issues: - Ensure Questions are pinned in memory when sending them through PAM. - Hold on to the PAM conversation struct after we build it. (Linux-PAM is leninent about this and copies the pam_conv structure.)
author Paul Fisher <paul@pfish.zone>
date Tue, 24 Jun 2025 17:54:33 -0400
parents b87100c5eed4
children 94eb11cb1798
comparison
equal deleted inserted replaced
100:3f11b8d30f63 101:94b51fa4f797
9 use crate::logging::Level; 9 use crate::logging::Level;
10 use crate::{Conversation, EnvironMap, Flags, PamHandleApplication, PamHandleModule}; 10 use crate::{Conversation, EnvironMap, Flags, PamHandleApplication, PamHandleModule};
11 use num_enum::{IntoPrimitive, TryFromPrimitive}; 11 use num_enum::{IntoPrimitive, TryFromPrimitive};
12 use std::cell::Cell; 12 use std::cell::Cell;
13 use std::ffi::{c_char, c_int, CString}; 13 use std::ffi::{c_char, c_int, CString};
14 use std::marker::PhantomData;
15 use std::ops::{Deref, DerefMut}; 14 use std::ops::{Deref, DerefMut};
16 use std::ptr; 15 use std::ptr;
17 16
18 /// Owner for a PAM handle. 17 /// Owner for a PAM handle.
19 struct HandleWrap(*mut LibPamHandle); 18 struct HandleWrap(*mut LibPamHandle);
31 } 30 }
32 } 31 }
33 32
34 /// An owned PAM handle. 33 /// An owned PAM handle.
35 pub struct OwnedLibPamHandle<'a> { 34 pub struct OwnedLibPamHandle<'a> {
35 /// The handle itself.
36 handle: HandleWrap, 36 handle: HandleWrap,
37 /// The last return value from the handle.
37 last_return: Cell<Result<()>>, 38 last_return: Cell<Result<()>>,
38 _conversation_lifetime: PhantomData<&'a mut ()>, 39 /// If set, the Conversation that this PAM handle owns.
40 conversation: Option<Box<LibPamConversation<'a>>>,
39 } 41 }
40 42
41 #[derive(Debug, PartialEq)] 43 #[derive(Debug, PartialEq)]
42 pub struct HandleBuilder { 44 pub struct HandleBuilder {
43 service_name: String, 45 service_name: String,
75 fn start( 77 fn start(
76 service_name: String, 78 service_name: String,
77 username: Option<String>, 79 username: Option<String>,
78 conversation: &impl Conversation, 80 conversation: &impl Conversation,
79 ) -> Result<Self> { 81 ) -> Result<Self> {
80 let conv = LibPamConversation::wrap(conversation); 82 let conv = Box::new(LibPamConversation::wrap(conversation));
81 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?; 83 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?;
82 let username_cstr = memory::prompt_ptr(memory::option_cstr(username.as_deref())?.as_ref()); 84 let username_cstr = memory::prompt_ptr(memory::option_cstr(username.as_deref())?.as_ref());
83 85
84 let mut handle: *mut LibPamHandle = ptr::null_mut(); 86 let mut handle: *mut LibPamHandle = ptr::null_mut();
85 // SAFETY: We've set everything up properly to call `pam_start`. 87 // SAFETY: We've set everything up properly to call `pam_start`.
86 // The returned value will be a valid pointer provided the result is OK. 88 // The returned value will be a valid pointer provided the result is OK.
87 let result = 89 let result = unsafe {
88 unsafe { pam_ffi::pam_start(service_cstr.as_ptr(), username_cstr, &conv, &mut handle) }; 90 pam_ffi::pam_start(
91 service_cstr.as_ptr(),
92 username_cstr,
93 conv.as_ref(),
94 &mut handle,
95 )
96 };
89 ErrorCode::result_from(result)?; 97 ErrorCode::result_from(result)?;
90 Ok(Self { 98 Ok(Self {
91 handle: HandleWrap(handle), 99 handle: HandleWrap(handle),
92 last_return: Cell::new(Ok(())), 100 last_return: Cell::new(Ok(())),
93 _conversation_lifetime: Default::default(), 101 conversation: Some(conv),
94 }) 102 })
95 } 103 }
96 } 104 }
97 105
98 impl PamHandleApplication for OwnedLibPamHandle<'_> { 106 impl PamHandleApplication for OwnedLibPamHandle<'_> {