comparison src/libpam/handle.rs @ 98:b87100c5eed4

Start on environment variables, and make pointers nicer. This starts work on the PAM environment handling, and in so doing, introduces the CHeapBox and CHeapString structs. These are analogous to Box and CString, but they're located on the C heap rather than being Rust-managed memory. This is because environment variables deal with even more pointers and it turns out we can lose a lot of manual freeing using homemade smart pointers.
author Paul Fisher <paul@pfish.zone>
date Tue, 24 Jun 2025 04:25:25 -0400
parents efe2f5f8b5b2
children 94b51fa4f797
comparison
equal deleted inserted replaced
97:efe2f5f8b5b2 98:b87100c5eed4
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::Message;
4 use crate::environ::EnvironMapMut;
4 use crate::handle::PamShared; 5 use crate::handle::PamShared;
6 use crate::libpam::environ::{LibPamEnviron, LibPamEnvironMut};
5 pub use crate::libpam::pam_ffi::LibPamHandle; 7 pub use crate::libpam::pam_ffi::LibPamHandle;
6 use crate::libpam::{memory, pam_ffi}; 8 use crate::libpam::{memory, pam_ffi};
7 use crate::logging::Level; 9 use crate::logging::Level;
8 use crate::{Conversation, Flags, PamHandleApplication, PamHandleModule}; 10 use crate::{Conversation, EnvironMap, Flags, PamHandleApplication, PamHandleModule};
9 use num_enum::{IntoPrimitive, TryFromPrimitive}; 11 use num_enum::{IntoPrimitive, TryFromPrimitive};
10 use std::cell::Cell; 12 use std::cell::Cell;
11 use std::ffi::{c_char, c_int, CString}; 13 use std::ffi::{c_char, c_int, CString};
12 use std::marker::PhantomData; 14 use std::marker::PhantomData;
13 use std::ops::{Deref, DerefMut}; 15 use std::ops::{Deref, DerefMut};
43 } 45 }
44 46
45 impl HandleBuilder { 47 impl HandleBuilder {
46 /// Creates a new HandleBuilder for the given service. 48 /// Creates a new HandleBuilder for the given service.
47 fn new(service_name: String) -> Self { 49 fn new(service_name: String) -> Self {
48 Self{service_name, username: Default::default()} 50 Self {
51 service_name,
52 username: Default::default(),
53 }
49 } 54 }
50 /// Updates the service name. 55 /// Updates the service name.
51 pub fn service_name(mut self, service_name: String) -> Self { 56 pub fn service_name(mut self, service_name: String) -> Self {
52 self.service_name = service_name; self 57 self.service_name = service_name;
58 self
53 } 59 }
54 /// Updates the username. 60 /// Updates the username.
55 pub fn username(mut self, username: String) -> Self { 61 pub fn username(mut self, username: String) -> Self {
56 self.username = Some(username); self 62 self.username = Some(username);
63 self
57 } 64 }
58 65
59 pub fn build(self, conv: &impl Conversation) -> Result<OwnedLibPamHandle> { 66 pub fn build(self, conv: &impl Conversation) -> Result<OwnedLibPamHandle> {
60 OwnedLibPamHandle::start(self.service_name, self.username, conv) 67 OwnedLibPamHandle::start(self.service_name, self.username, conv)
61 } 68 }
63 70
64 impl OwnedLibPamHandle<'_> { 71 impl OwnedLibPamHandle<'_> {
65 pub fn build_with_service(service_name: String) -> HandleBuilder { 72 pub fn build_with_service(service_name: String) -> HandleBuilder {
66 HandleBuilder::new(service_name) 73 HandleBuilder::new(service_name)
67 } 74 }
68 fn start(service_name: String, username: Option<String>, conversation: &impl Conversation) -> Result<Self> { 75 fn start(
76 service_name: String,
77 username: Option<String>,
78 conversation: &impl Conversation,
79 ) -> Result<Self> {
69 let conv = LibPamConversation::wrap(conversation); 80 let conv = LibPamConversation::wrap(conversation);
70 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?; 81 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?;
71 let username_cstr = memory::prompt_ptr(memory::option_cstr(username.as_deref())?.as_ref()); 82 let username_cstr = memory::prompt_ptr(memory::option_cstr(username.as_deref())?.as_ref());
72 83
73 let mut handle: *mut LibPamHandle = ptr::null_mut(); 84 let mut handle: *mut LibPamHandle = ptr::null_mut();
74 // SAFETY: We've set everything up properly to call `pam_start`. 85 // SAFETY: We've set everything up properly to call `pam_start`.
75 // The returned value will be a valid pointer provided the result is OK. 86 // The returned value will be a valid pointer provided the result is OK.
76 let result = unsafe { pam_ffi::pam_start(service_cstr.as_ptr(), username_cstr, &conv, &mut handle) }; 87 let result =
88 unsafe { pam_ffi::pam_start(service_cstr.as_ptr(), username_cstr, &conv, &mut handle) };
77 ErrorCode::result_from(result)?; 89 ErrorCode::result_from(result)?;
78 Ok(Self{ 90 Ok(Self {
79 handle: HandleWrap(handle), 91 handle: HandleWrap(handle),
80 last_return: Cell::new(Ok(())), 92 last_return: Cell::new(Ok(())),
81 _conversation_lifetime: Default::default(), 93 _conversation_lifetime: Default::default(),
82 }) 94 })
83 } 95 }
84 } 96 }
85 97
86 impl PamHandleApplication for OwnedLibPamHandle<'_> { 98 impl PamHandleApplication for OwnedLibPamHandle<'_> {
87 fn authenticate(&mut self, flags: Flags) -> Result<()> { 99 fn authenticate(&mut self, flags: Flags) -> Result<()> {
88 let ret = unsafe { pam_ffi::pam_authenticate(self.handle.0, flags.bits() as c_int)}; 100 let ret = unsafe { pam_ffi::pam_authenticate(self.handle.0, flags.bits() as c_int) };
89 let result = ErrorCode::result_from(ret); 101 let result = ErrorCode::result_from(ret);
90 self.last_return.set(result); 102 self.last_return.set(result);
91 result 103 result
92 } 104 }
93 105
94 fn account_management(&mut self, flags: Flags) -> Result<()> { 106 fn account_management(&mut self, flags: Flags) -> Result<()> {
95 let ret = unsafe { pam_ffi::pam_acct_mgmt(self.handle.0, flags.bits() as c_int)}; 107 let ret = unsafe { pam_ffi::pam_acct_mgmt(self.handle.0, flags.bits() as c_int) };
96 let result = ErrorCode::result_from(ret); 108 let result = ErrorCode::result_from(ret);
97 self.last_return.set(result); 109 self.last_return.set(result);
98 result 110 result
99 } 111 }
100 112
101 fn change_authtok(&mut self, flags: Flags) -> Result<()> { 113 fn change_authtok(&mut self, flags: Flags) -> Result<()> {
102 let ret = unsafe { pam_ffi::pam_chauthtok(self.handle.0, flags.bits() as c_int)}; 114 let ret = unsafe { pam_ffi::pam_chauthtok(self.handle.0, flags.bits() as c_int) };
103 let result = ErrorCode::result_from(ret); 115 let result = ErrorCode::result_from(ret);
104 self.last_return.set(result); 116 self.last_return.set(result);
105 result 117 result
106 } 118 }
107 } 119 }
176 }; 188 };
177 ErrorCode::result_from(ret)?; 189 ErrorCode::result_from(ret)?;
178 unsafe { memory::copy_pam_string(output) } 190 unsafe { memory::copy_pam_string(output) }
179 .transpose() 191 .transpose()
180 .unwrap_or(Err(ErrorCode::ConversationError)) 192 .unwrap_or(Err(ErrorCode::ConversationError))
193 }
194
195 fn environ(&self) -> impl EnvironMap {
196 LibPamEnviron::new(self)
197 }
198
199 fn environ_mut(&mut self) -> impl EnvironMapMut {
200 LibPamEnvironMut::new(self)
181 } 201 }
182 202
183 cstr_item!(get = user_item, item = ItemType::User); 203 cstr_item!(get = user_item, item = ItemType::User);
184 cstr_item!(set = set_user_item, item = ItemType::User); 204 cstr_item!(set = set_user_item, item = ItemType::User);
185 cstr_item!(get = service, item = ItemType::Service); 205 cstr_item!(get = service, item = ItemType::Service);
288 unsafe { output.as_mut() }.ok_or(ErrorCode::ConversationError) 308 unsafe { output.as_mut() }.ok_or(ErrorCode::ConversationError)
289 } 309 }
290 } 310 }
291 311
292 macro_rules! delegate { 312 macro_rules! delegate {
313 // First have the kind that save the result after delegation.
293 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => { 314 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => {
294 fn $meth(&self $(, $param: $typ)*) -> Result<$ret> { 315 fn $meth(&self $(, $param: $typ)*) -> Result<$ret> {
295 let result = self.handle.$meth($($param),*); 316 let result = self.handle.$meth($($param),*);
296 self.last_return.set(split(&result)); 317 self.last_return.set(split(&result));
297 result 318 result
302 let result = self.handle.$meth($($param),*); 323 let result = self.handle.$meth($($param),*);
303 self.last_return.set(split(&result)); 324 self.last_return.set(split(&result));
304 result 325 result
305 } 326 }
306 }; 327 };
328 // Then have the kind that are just raw delegates
329 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> $ret:ty) => {
330 fn $meth(&self $(, $param: $typ)*) -> $ret {
331 self.handle.$meth($($param),*)
332 }
333 };
334 (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> $ret:ty) => {
335 fn $meth(&mut self $(, $param: $typ)*) -> $ret {
336 self.handle.$meth($($param),*)
337 }
338 };
339 // Then have item getters / setters
307 (get = $get:ident$(, set = $set:ident)?) => { 340 (get = $get:ident$(, set = $set:ident)?) => {
308 delegate!(fn $get(&self) -> Result<Option<String>>); 341 delegate!(fn $get(&self) -> Result<Option<String>>);
309 $(delegate!(set = $set);)? 342 $(delegate!(set = $set);)?
310 }; 343 };
311 (set = $set:ident) => { 344 (set = $set:ident) => {
316 fn split<T>(result: &Result<T>) -> Result<()> { 349 fn split<T>(result: &Result<T>) -> Result<()> {
317 result.as_ref().map(drop).map_err(|&e| e) 350 result.as_ref().map(drop).map_err(|&e| e)
318 } 351 }
319 352
320 impl PamShared for OwnedLibPamHandle<'_> { 353 impl PamShared for OwnedLibPamHandle<'_> {
321 fn log(&self, level: Level, entry: &str) { 354 delegate!(fn log(&self, level: Level, entry: &str) -> ());
322 self.handle.log(level, entry) 355 delegate!(fn environ(&self) -> impl EnvironMap);
323 } 356 delegate!(fn environ_mut(&mut self) -> impl EnvironMapMut);
324 delegate!(fn username(&mut self, prompt: Option<&str>) -> Result<String>); 357 delegate!(fn username(&mut self, prompt: Option<&str>) -> Result<String>);
325 delegate!(get = user_item, set = set_user_item); 358 delegate!(get = user_item, set = set_user_item);
326 delegate!(get = service, set = set_service); 359 delegate!(get = service, set = set_service);
327 delegate!(get = user_prompt, set = set_user_prompt); 360 delegate!(get = user_prompt, set = set_user_prompt);
328 delegate!(get = tty_name, set = set_tty_name); 361 delegate!(get = tty_name, set = set_tty_name);