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)?;