comparison src/libpam/handle.rs @ 141:a508a69c068a

Remove a lot of Results from functions. Many functions are documented to only return failing Results when given improper inputs or when there is a memory allocation failure (which can be verified by looking at the source). In cases where we know our input is correct, we don't need to check for memory allocation errors for the same reason that Rust doesn't do so when you, e.g., create a new Vec.
author Paul Fisher <paul@pfish.zone>
date Sat, 05 Jul 2025 17:16:56 -0400
parents 6c1e1bdb4164
children ebb71a412b58
comparison
equal deleted inserted replaced
140:add7228adb2f 141:a508a69c068a
1 use super::conversation::LibPamConversation; 1 use super::conversation::{OwnedConversation, PamConv};
2 use crate::constants::{ErrorCode, Result}; 2 use crate::constants::{ErrorCode, Result};
3 use crate::conv::Exchange; 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};
8 use crate::logging::{Level, Location}; 8 use crate::logging::{Level, Location};
9 use crate::{ 9 use crate::{
10 guide, linklist, stdlinks, Conversation, EnvironMap, Flags, PamHandleApplication, 10 guide, linklist, stdlinks, Conversation, EnvironMap, Flags, PamHandleApplication,
11 PamHandleModule, 11 PamHandleModule,
12 }; 12 };
13 use libpam_sys_helpers::constants;
13 use num_enum::{IntoPrimitive, TryFromPrimitive}; 14 use num_enum::{IntoPrimitive, TryFromPrimitive};
14 use std::cell::Cell; 15 use std::cell::Cell;
15 use std::ffi::{c_char, c_int, CString}; 16 use std::ffi::{c_char, c_int, CString};
16 17 use std::mem::ManuallyDrop;
17 use std::ptr; 18 use std::ptr;
18 use std::ptr::NonNull; 19 use std::ptr::NonNull;
19 20
20 /// Owner for a PAM handle.
21 pub struct LibPamHandle(pub NonNull<libpam_sys::pam_handle>);
22
23 impl AsRef<libpam_sys::pam_handle> for LibPamHandle {
24 fn as_ref(&self) -> &libpam_sys::pam_handle {
25 unsafe { self.0.as_ref() }
26 }
27 }
28
29 impl AsMut<libpam_sys::pam_handle> for LibPamHandle {
30 fn as_mut(&mut self) -> &mut libpam_sys::pam_handle {
31 unsafe { self.0.as_mut() }
32 }
33 }
34
35 /// An owned PAM handle. 21 /// An owned PAM handle.
36 pub struct OwnedLibPamHandle<'a> { 22 pub struct OwnedLibPamHandle<C: Conversation> {
37 /// The handle itself. 23 /// The handle itself.
38 handle: LibPamHandle, 24 handle: ManuallyDrop<RawPamHandle>,
39 /// The last return value from the handle. 25 /// The last return value from the handle.
40 last_return: Cell<Result<()>>, 26 last_return: Cell<Result<()>>,
41 /// If set, the Conversation that this PAM handle owns. 27 /// If set, the Conversation that this PAM handle owns.
42 /// 28 ///
43 /// We have to hold on to this because the PAM specification doesn't 29 /// We have to hold on to this because the PAM specification doesn't
44 /// actually say what the PAM library should do with a passed-in 30 /// actually say what the PAM library should do with a passed-in
45 /// conversation. Linux-PAM copies the contents of the `pam_conv` struct 31 /// conversation. Linux-PAM copies the contents of the `pam_conv` struct
46 /// that you pass in to `pam_start`, but OpenPAM uses the pointer itself, 32 /// that you pass in to `pam_start`, but OpenPAM uses the pointer itself,
47 /// so you have to keep it in one place. 33 /// so you have to keep it in one place.
48 conversation: Option<Box<LibPamConversation<'a>>>, 34 conversation: Box<OwnedConversation<C>>,
49 } 35 }
50 36
51 #[derive(Debug, PartialEq)] 37 #[derive(Debug, PartialEq)]
52 pub struct HandleBuilder { 38 pub struct HandleBuilder {
53 service_name: String, 39 service_name: String,
66 pub fn username(mut self, username: String) -> Self { 52 pub fn username(mut self, username: String) -> Self {
67 self.username = Some(username); 53 self.username = Some(username);
68 self 54 self
69 } 55 }
70 /// Builds a PAM handle and starts the transaction. 56 /// Builds a PAM handle and starts the transaction.
71 pub fn build(self, conv: &impl Conversation) -> Result<OwnedLibPamHandle> { 57 pub fn build(self, conv: impl Conversation) -> Result<OwnedLibPamHandle<impl Conversation>> {
72 OwnedLibPamHandle::start(self.service_name, self.username, conv) 58 OwnedLibPamHandle::start(self.service_name, self.username, conv)
73 } 59 }
74 } 60 }
75 61
76 impl OwnedLibPamHandle<'_> { 62 impl<C: Conversation> OwnedLibPamHandle<C> {
77 /// Creates a builder to start a PAM transaction for the given service. 63 /// Creates a builder to start a PAM transaction for the given service.
78 /// 64 ///
79 /// The service name is what controls the steps and checks PAM goes through 65 /// The service name is what controls the steps and checks PAM goes through
80 /// when authenticating a user. This corresponds to the configuration file 66 /// when authenticating a user. This corresponds to the configuration file
81 /// named <code>/etc/pam.d/<var>service_name</var></code>. 67 /// named <code>/etc/pam.d/<var>service_name</var></code>.
90 service_name, 76 service_name,
91 username: None, 77 username: None,
92 } 78 }
93 } 79 }
94 80
95 fn start( 81 fn start(service_name: String, username: Option<String>, conversation: C) -> Result<Self> {
96 service_name: String, 82 let conv = Box::new(OwnedConversation::new(conversation));
97 username: Option<String>,
98 conversation: &impl Conversation,
99 ) -> Result<Self> {
100 let conv = Box::new(LibPamConversation::wrap(conversation));
101 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?; 83 let service_cstr = CString::new(service_name).map_err(|_| ErrorCode::ConversationError)?;
102 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());
103 85
104 let mut handle: *mut libpam_sys::pam_handle = ptr::null_mut(); 86 let mut handle: *mut libpam_sys::pam_handle = ptr::null_mut();
105 // SAFETY: We've set everything up properly to call `pam_start`. 87 // SAFETY: We've set everything up properly to call `pam_start`.
106 // 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.
107 let result = unsafe { 89 let result = unsafe {
108 libpam_sys::pam_start( 90 libpam_sys::pam_start(
109 service_cstr.as_ptr(), 91 service_cstr.as_ptr(),
110 username_cstr, 92 username_cstr,
111 (conv.as_ref() as *const LibPamConversation) 93 (conv.as_ref() as *const OwnedConversation<C>)
112 .cast_mut() 94 .cast_mut()
113 .cast(), 95 .cast(),
114 &mut handle, 96 &mut handle,
115 ) 97 )
116 }; 98 };
117 ErrorCode::result_from(result)?; 99 ErrorCode::result_from(result)?;
118 let handle = NonNull::new(handle).ok_or(ErrorCode::BufferError)?; 100 let handle = NonNull::new(handle).ok_or(ErrorCode::BufferError)?;
119 Ok(Self { 101 Ok(Self {
120 handle: LibPamHandle(handle), 102 handle: ManuallyDrop::new(RawPamHandle(handle)),
121 last_return: Cell::new(Ok(())), 103 last_return: Cell::new(Ok(())),
122 conversation: Some(conv), 104 conversation: conv,
123 }) 105 })
124 } 106 }
125 } 107
126 108 /// "Quietly" closes the PAM session on an owned PAM handle.
127 impl PamHandleApplication for OwnedLibPamHandle<'_> { 109 ///
128 fn authenticate(&mut self, flags: Flags) -> Result<()> { 110 /// This internally calls `pam_end` with the appropriate error code.
129 let ret = 111 ///
130 unsafe { libpam_sys::pam_authenticate(self.handle.as_mut(), flags.bits() as c_int) }; 112 /// # References
131 let result = ErrorCode::result_from(ret); 113 #[doc = linklist!(pam_end: adg, _std)]
132 self.last_return.set(result); 114 ///
133 result 115 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")]
134 } 116 #[doc = stdlinks!(3 pam_end)]
135 117
136 fn account_management(&mut self, flags: Flags) -> Result<()> { 118 fn end_quiet(self) {}
137 let ret = unsafe { libpam_sys::pam_acct_mgmt(self.handle.as_mut(), flags.bits() as c_int) }; 119 }
138 let result = ErrorCode::result_from(ret); 120
139 self.last_return.set(result); 121 macro_rules! wrap {
140 result 122 (fn $name:ident { $pam_func:ident }) => {
141 } 123 fn $name(&mut self, flags: Flags) -> Result<()> {
142 124 ErrorCode::result_from(unsafe { libpam_sys::$pam_func(self.0.as_mut(), flags.bits()) })
143 fn change_authtok(&mut self, flags: Flags) -> Result<()> { 125 }
144 let ret = unsafe { libpam_sys::pam_chauthtok(self.handle.as_mut(), flags.bits() as c_int) }; 126 };
145 let result = ErrorCode::result_from(ret); 127 }
146 self.last_return.set(result); 128
147 result 129 impl PamHandleApplication for RawPamHandle {
148 } 130 wrap!(fn authenticate { pam_authenticate });
131 wrap!(fn account_management { pam_acct_mgmt });
132 wrap!(fn change_authtok { pam_chauthtok });
149 } 133 }
150 134
151 // TODO: pam_authenticate - app 135 // TODO: pam_authenticate - app
152 // pam_setcred - app 136 // pam_setcred - app
153 // pam_acct_mgmt - app 137 // pam_acct_mgmt - app
156 // pam_close_session - app 140 // pam_close_session - app
157 // pam_putenv - shared 141 // pam_putenv - shared
158 // pam_getenv - shared 142 // pam_getenv - shared
159 // pam_getenvlist - shared 143 // pam_getenvlist - shared
160 144
161 impl Drop for OwnedLibPamHandle<'_> { 145 impl<C: Conversation> Drop for OwnedLibPamHandle<C> {
162 /// Closes the PAM session on an owned PAM handle. 146 /// Closes the PAM session on an owned PAM handle.
163 /// 147 ///
164 /// This internally calls `pam_end` with the appropriate error code. 148 /// This internally calls `pam_end` with the appropriate error code.
165 /// 149 ///
166 /// # References 150 /// # References
169 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] 153 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")]
170 #[doc = stdlinks!(3 pam_end)] 154 #[doc = stdlinks!(3 pam_end)]
171 fn drop(&mut self) { 155 fn drop(&mut self) {
172 unsafe { 156 unsafe {
173 libpam_sys::pam_end( 157 libpam_sys::pam_end(
174 self.handle.as_mut(), 158 self.handle.raw_mut(),
175 ErrorCode::result_to_c(self.last_return.get()), 159 ErrorCode::result_to_c(self.last_return.get()),
176 ); 160 );
177 } 161 }
178 } 162 }
163 }
164
165 macro_rules! delegate {
166 // First have the kind that save the result after delegation.
167 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => {
168 fn $meth(&self $(, $param: $typ)*) -> Result<$ret> {
169 let result = self.handle.$meth($($param),*);
170 self.last_return.set(split(&result));
171 result
172 }
173 };
174 (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => {
175 fn $meth(&mut self $(, $param: $typ)*) -> Result<$ret> {
176 let result = self.handle.$meth($($param),*);
177 self.last_return.set(split(&result));
178 result
179 }
180 };
181 // Then have the kind that are just raw delegates
182 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> $ret:ty) => {
183 fn $meth(&self $(, $param: $typ)*) -> $ret {
184 self.handle.$meth($($param),*)
185 }
186 };
187 (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> $ret:ty) => {
188 fn $meth(&mut self $(, $param: $typ)*) -> $ret {
189 self.handle.$meth($($param),*)
190 }
191 };
192 // Then have item getters / setters
193 (get = $get:ident$(, set = $set:ident)?) => {
194 delegate!(fn $get(&self) -> Result<Option<String>>);
195 $(delegate!(set = $set);)?
196 };
197 (set = $set:ident) => {
198 delegate!(fn $set(&mut self, value: Option<&str>) -> Result<()>);
199 };
200 }
201
202 fn split<T>(result: &Result<T>) -> Result<()> {
203 result.as_ref().map(drop).map_err(|&e| e)
204 }
205
206 impl<C: Conversation> PamShared for OwnedLibPamHandle<C> {
207 delegate!(fn log(&self, level: Level, location: Location<'_>, entry: &str) -> ());
208 delegate!(fn environ(&self) -> impl EnvironMap);
209 delegate!(fn environ_mut(&mut self) -> impl EnvironMapMut);
210 delegate!(fn username(&mut self, prompt: Option<&str>) -> Result<String>);
211 delegate!(get = user_item, set = set_user_item);
212 delegate!(get = service, set = set_service);
213 delegate!(get = user_prompt, set = set_user_prompt);
214 delegate!(get = tty_name, set = set_tty_name);
215 delegate!(get = remote_user, set = set_remote_user);
216 delegate!(get = remote_host, set = set_remote_host);
217 delegate!(set = set_authtok_item);
218 delegate!(set = set_old_authtok_item);
179 } 219 }
180 220
181 /// Macro to implement getting/setting a CStr-based item. 221 /// Macro to implement getting/setting a CStr-based item.
182 macro_rules! cstr_item { 222 macro_rules! cstr_item {
183 (get = $getter:ident, item = $item_type:path) => { 223 (get = $getter:ident, item = $item_type:path) => {
190 unsafe { self.set_cstr_item($item_type, value) } 230 unsafe { self.set_cstr_item($item_type, value) }
191 } 231 }
192 }; 232 };
193 } 233 }
194 234
195 impl PamShared for LibPamHandle { 235 /// An owned variation of a basic PAM handle.
236 ///
237 /// This is the most basic version of a wrapped PAM handle. It's mostly used
238 /// as the inside of the [`OwnedLibPamHandle`], but can also be used to "adopt"
239 /// a PAM handle created by another library.
240 ///
241 /// If [`Self::end`] is not called, this will always call `pam_end` reporting
242 /// successful completion.
243 pub struct RawPamHandle(NonNull<libpam_sys::pam_handle>);
244
245 impl RawPamHandle {
246 /// Takes ownership of the pointer to the given PAM handle.
247 ///
248 /// **Do not use this just to get a reference to a PAM handle.**
249 ///
250 /// # Safety
251 ///
252 /// - The pointer must point to a valid PAM handle.
253 /// - The conversation associated with the handle must remain valid
254 /// for as long as the handle is open.
255 pub unsafe fn from_ptr(handle: NonNull<libpam_sys::pam_handle>) -> Self {
256 Self(handle)
257 }
258
259 /// Ends the transaction, reporting `error_code` to cleanup callbacks.
260 ///
261 /// # References
262 #[doc = linklist!(pam_end: adg, _std)]
263 ///
264 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")]
265 #[doc = stdlinks!(3 pam_end)]
266 pub fn end(self, result: Result<()>) {
267 let mut me = ManuallyDrop::new(self);
268 unsafe { libpam_sys::pam_end(me.raw_mut(), ErrorCode::result_to_c(result)) };
269 }
270
271 #[cfg_attr(
272 not(pam_impl = "LinuxPam"),
273 doc = "Exactly equivalent to [`Self::end`], except on Linux-PAM."
274 )]
275 #[cfg_attr(
276 pam_impl = "LinuxPam",
277 doc = "Ends the transaction \"quietly\", reporting `error_code` to cleanup callbacks."
278 )]
279 ///
280 /// On Linux-PAM only, this sets the
281 /// [`PAM_DATA_SILENT`](libpam_sys::PAM_DATA_SILENT) flag on the flags
282 /// passed to the cleanup callbacks. This conventionally means that this
283 /// `pam_end` call is occurring on a forked process, and that a session
284 /// may still be open on the parent process, and modules "should not treat
285 /// the call too seriously".
286 ///
287 /// # References
288 #[doc = linklist!(pam_end: adg, _std)]
289 ///
290 #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")]
291 #[doc = stdlinks!(3 pam_end)]
292 pub fn end_quiet(self, result: Result<()>) {
293 let mut me = ManuallyDrop::new(self);
294 let result = ErrorCode::result_to_c(result);
295 #[cfg(pam_impl = "LinuxPam")]
296 let result = result | libpam_sys::PAM_DATA_SILENT;
297 unsafe {
298 libpam_sys::pam_end(me.raw_mut(), result);
299 }
300 }
301
302 /// Consumes this and gives you back the raw PAM handle.
303 pub fn into_inner(self) -> NonNull<libpam_sys::pam_handle> {
304 let me = ManuallyDrop::new(self);
305 me.0
306 }
307
308 /// Gets a reference to the inner PAM handle.
309 pub fn raw_ref(&self) -> &libpam_sys::pam_handle {
310 unsafe { self.0.as_ref() }
311 }
312 /// Gets a mutable reference to the inner PAM handle.
313 pub fn raw_mut(&mut self) -> &mut libpam_sys::pam_handle {
314 unsafe { self.0.as_mut() }
315 }
316 }
317
318 impl Drop for RawPamHandle {
319 fn drop(&mut self) {
320 unsafe { libpam_sys::pam_end(self.0.as_mut(), 0) };
321 }
322 }
323
324 impl PamShared for RawPamHandle {
196 #[cfg(any())] 325 #[cfg(any())]
197 fn log(&self, level: Level, loc: Location<'_>, entry: &str) { 326 fn log(&self, level: Level, loc: Location<'_>, entry: &str) {
198 let entry = match CString::new(entry).or_else(|_| CString::new(dbg!(entry))) { 327 let entry = match CString::new(entry).or_else(|_| CString::new(dbg!(entry))) {
199 Ok(cstr) => cstr, 328 Ok(cstr) => cstr,
200 _ => return, 329 _ => return,
227 fn username(&mut self, prompt: Option<&str>) -> Result<String> { 356 fn username(&mut self, prompt: Option<&str>) -> Result<String> {
228 let prompt = memory::option_cstr(prompt)?; 357 let prompt = memory::option_cstr(prompt)?;
229 let mut output: *const c_char = ptr::null(); 358 let mut output: *const c_char = ptr::null();
230 let ret = unsafe { 359 let ret = unsafe {
231 libpam_sys::pam_get_user( 360 libpam_sys::pam_get_user(
232 self.as_mut(), 361 self.raw_mut(),
233 &mut output, 362 &mut output,
234 memory::prompt_ptr(prompt.as_ref()), 363 memory::prompt_ptr(prompt.as_ref()),
235 ) 364 )
236 }; 365 };
237 ErrorCode::result_from(ret)?; 366 ErrorCode::result_from(ret)?;
262 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost); 391 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost);
263 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); 392 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok);
264 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); 393 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok);
265 } 394 }
266 395
267 impl Conversation for LibPamHandle { 396 impl Conversation for RawPamHandle {
268 fn communicate(&self, messages: &[Exchange]) { 397 fn communicate(&self, messages: &[Exchange]) {
269 match self.conversation_item() { 398 match self.conversation_item() {
270 Ok(conv) => conv.communicate(messages), 399 Ok(conv) => conv.communicate(messages),
271 Err(e) => { 400 Err(e) => {
272 for msg in messages { 401 for msg in messages {
275 } 404 }
276 } 405 }
277 } 406 }
278 } 407 }
279 408
280 impl PamHandleModule for LibPamHandle { 409 impl PamHandleModule for RawPamHandle {
410 fn authtok(&mut self, prompt: Option<&str>) -> Result<String> {
411 self.get_authtok(prompt, ItemType::AuthTok)
412 }
413
414 fn old_authtok(&mut self, prompt: Option<&str>) -> Result<String> {
415 self.get_authtok(prompt, ItemType::OldAuthTok)
416 }
417
418 cstr_item!(get = authtok_item, item = ItemType::AuthTok);
419 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok);
420 }
421
422 /// Function called at the end of a PAM session that is called to clean up
423 /// a value previously provided to PAM in a `pam_set_data` call.
424 ///
425 /// You should never call this yourself.
426 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) {
427 unsafe {
428 let _data: Box<T> = Box::from_raw(c_data.cast());
429 }
430 }
431
432 // Implementations of internal functions.
433 impl RawPamHandle {
281 #[cfg(any(pam_impl = "LinuxPam", pam_impl = "OpenPam"))] 434 #[cfg(any(pam_impl = "LinuxPam", pam_impl = "OpenPam"))]
282 fn authtok(&mut self, prompt: Option<&str>) -> Result<String> { 435 fn get_authtok(&mut self, prompt: Option<&str>, item_type: ItemType) -> Result<String> {
283 let prompt = memory::option_cstr(prompt)?; 436 let prompt = memory::option_cstr(prompt)?;
284 let mut output: *const c_char = ptr::null_mut(); 437 let mut output: *const c_char = ptr::null_mut();
285 // SAFETY: We're calling this with known-good values. 438 // SAFETY: We're calling this with known-good values.
286 let res = unsafe { 439 let res = unsafe {
287 libpam_sys::pam_get_authtok( 440 libpam_sys::pam_get_authtok(
288 self.as_mut(), 441 self.raw_mut(),
289 ItemType::AuthTok.into(), 442 item_type.into(),
290 &mut output, 443 &mut output,
291 memory::prompt_ptr(prompt.as_ref()), 444 memory::prompt_ptr(prompt.as_ref()),
292 ) 445 )
293 }; 446 };
294 ErrorCode::result_from(res)?; 447 ErrorCode::result_from(res)?;
297 .transpose() 450 .transpose()
298 .unwrap_or(Err(ErrorCode::ConversationError)) 451 .unwrap_or(Err(ErrorCode::ConversationError))
299 } 452 }
300 453
301 #[cfg(not(any(pam_impl = "LinuxPam", pam_impl = "OpenPam")))] 454 #[cfg(not(any(pam_impl = "LinuxPam", pam_impl = "OpenPam")))]
302 fn authtok(&mut self, prompt: Option<&str>) -> Result<String> { 455 fn get_authtok(&mut self, prompt: Option<&str>, item_type: ItemType) -> Result<String> {
303 Err(ErrorCode::ConversationError) 456 Err(ErrorCode::ConversationError)
304 } 457 }
305 458
306 cstr_item!(get = authtok_item, item = ItemType::AuthTok);
307 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok);
308 }
309
310 /// Function called at the end of a PAM session that is called to clean up
311 /// a value previously provided to PAM in a `pam_set_data` call.
312 ///
313 /// You should never call this yourself.
314 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) {
315 unsafe {
316 let _data: Box<T> = Box::from_raw(c_data.cast());
317 }
318 }
319
320 impl LibPamHandle {
321 /// Gets a C string item. 459 /// Gets a C string item.
322 /// 460 ///
323 /// # Safety 461 /// # Safety
324 /// 462 ///
325 /// You better be requesting an item which is a C string. 463 /// You better be requesting an item which is a C string.
326 unsafe fn get_cstr_item(&self, item_type: ItemType) -> Result<Option<String>> { 464 unsafe fn get_cstr_item(&self, item_type: ItemType) -> Result<Option<String>> {
327 let mut output = ptr::null(); 465 let mut output = ptr::null();
328 let ret = 466 let ret =
329 unsafe { libpam_sys::pam_get_item(self.as_ref(), item_type as c_int, &mut output) }; 467 unsafe { libpam_sys::pam_get_item(self.raw_ref(), item_type as c_int, &mut output) };
330 ErrorCode::result_from(ret)?; 468 ErrorCode::result_from(ret)?;
331 memory::copy_pam_string(output.cast()) 469 memory::copy_pam_string(output.cast())
332 } 470 }
333 471
334 /// Sets a C string item. 472 /// Sets a C string item.
338 /// You better be setting an item which is a C string. 476 /// You better be setting an item which is a C string.
339 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> { 477 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> {
340 let data_str = memory::option_cstr(data)?; 478 let data_str = memory::option_cstr(data)?;
341 let ret = unsafe { 479 let ret = unsafe {
342 libpam_sys::pam_set_item( 480 libpam_sys::pam_set_item(
343 self.as_mut(), 481 self.raw_mut(),
344 item_type as c_int, 482 item_type as c_int,
345 memory::prompt_ptr(data_str.as_ref()).cast(), 483 memory::prompt_ptr(data_str.as_ref()).cast(),
346 ) 484 )
347 }; 485 };
348 ErrorCode::result_from(ret) 486 ErrorCode::result_from(ret)
349 } 487 }
350 488
351 /// Gets the `PAM_CONV` item from the handle. 489 /// Gets the `PAM_CONV` item from the handle.
352 fn conversation_item(&self) -> Result<&mut LibPamConversation<'_>> { 490 fn conversation_item(&self) -> Result<&PamConv> {
353 let output: *mut LibPamConversation = ptr::null_mut(); 491 let output: *const PamConv = ptr::null_mut();
354 let result = unsafe { 492 let result = unsafe {
355 libpam_sys::pam_get_item( 493 libpam_sys::pam_get_item(
356 self.as_ref(), 494 self.raw_ref(),
357 ItemType::Conversation.into(), 495 ItemType::Conversation.into(),
358 &mut output.cast_const().cast(), 496 &mut output.cast(),
359 ) 497 )
360 }; 498 };
361 ErrorCode::result_from(result)?; 499 ErrorCode::result_from(result)?;
362 // SAFETY: We got this result from PAM, and we're checking if it's null. 500 // SAFETY: We got this result from PAM, and we're checking if it's null.
363 unsafe { output.as_mut() }.ok_or(ErrorCode::ConversationError) 501 unsafe { output.as_ref() }.ok_or(ErrorCode::ConversationError)
364 } 502 }
365 }
366
367 macro_rules! delegate {
368 // First have the kind that save the result after delegation.
369 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => {
370 fn $meth(&self $(, $param: $typ)*) -> Result<$ret> {
371 let result = self.handle.$meth($($param),*);
372 self.last_return.set(split(&result));
373 result
374 }
375 };
376 (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => {
377 fn $meth(&mut self $(, $param: $typ)*) -> Result<$ret> {
378 let result = self.handle.$meth($($param),*);
379 self.last_return.set(split(&result));
380 result
381 }
382 };
383 // Then have the kind that are just raw delegates
384 (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> $ret:ty) => {
385 fn $meth(&self $(, $param: $typ)*) -> $ret {
386 self.handle.$meth($($param),*)
387 }
388 };
389 (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> $ret:ty) => {
390 fn $meth(&mut self $(, $param: $typ)*) -> $ret {
391 self.handle.$meth($($param),*)
392 }
393 };
394 // Then have item getters / setters
395 (get = $get:ident$(, set = $set:ident)?) => {
396 delegate!(fn $get(&self) -> Result<Option<String>>);
397 $(delegate!(set = $set);)?
398 };
399 (set = $set:ident) => {
400 delegate!(fn $set(&mut self, value: Option<&str>) -> Result<()>);
401 };
402 }
403
404 fn split<T>(result: &Result<T>) -> Result<()> {
405 result.as_ref().map(drop).map_err(|&e| e)
406 }
407
408 impl PamShared for OwnedLibPamHandle<'_> {
409 delegate!(fn log(&self, level: Level, location: Location<'_>, entry: &str) -> ());
410 delegate!(fn environ(&self) -> impl EnvironMap);
411 delegate!(fn environ_mut(&mut self) -> impl EnvironMapMut);
412 delegate!(fn username(&mut self, prompt: Option<&str>) -> Result<String>);
413 delegate!(get = user_item, set = set_user_item);
414 delegate!(get = service, set = set_service);
415 delegate!(get = user_prompt, set = set_user_prompt);
416 delegate!(get = tty_name, set = set_tty_name);
417 delegate!(get = remote_user, set = set_remote_user);
418 delegate!(get = remote_host, set = set_remote_host);
419 delegate!(set = set_authtok_item);
420 delegate!(set = set_old_authtok_item);
421 } 503 }
422 504
423 /// Identifies what is being gotten or set with `pam_get_item` 505 /// Identifies what is being gotten or set with `pam_get_item`
424 /// or `pam_set_item`. 506 /// or `pam_set_item`.
425 #[derive(TryFromPrimitive, IntoPrimitive)] 507 #[derive(TryFromPrimitive, IntoPrimitive)]
426 #[repr(i32)] 508 #[repr(i32)]
427 #[non_exhaustive] // because C could give us anything! 509 #[non_exhaustive] // because C could give us anything!
428 pub enum ItemType { 510 pub enum ItemType {
429 /// The PAM service name. 511 /// The PAM service name.
430 Service = 1, 512 Service = constants::PAM_SERVICE,
431 /// The user's login name. 513 /// The user's login name.
432 User = 2, 514 User = constants::PAM_USER,
433 /// The TTY name. 515 /// The TTY name.
434 Tty = 3, 516 Tty = constants::PAM_TTY,
435 /// The remote host (if applicable). 517 /// The remote host (if applicable).
436 RemoteHost = 4, 518 RemoteHost = constants::PAM_RHOST,
437 /// The conversation struct (not a CStr-based item). 519 /// The conversation struct (not a CStr-based item).
438 Conversation = 5, 520 Conversation = constants::PAM_CONV,
439 /// The authentication token (password). 521 /// The authentication token (password).
440 AuthTok = 6, 522 AuthTok = constants::PAM_AUTHTOK,
441 /// The old authentication token (when changing passwords). 523 /// The old authentication token (when changing passwords).
442 OldAuthTok = 7, 524 OldAuthTok = constants::PAM_OLDAUTHTOK,
443 /// The remote user's name. 525 /// The remote user's name.
444 RemoteUser = 8, 526 RemoteUser = constants::PAM_RUSER,
445 /// The prompt shown when requesting a username. 527 /// The prompt shown when requesting a username.
446 UserPrompt = 9, 528 UserPrompt = constants::PAM_USER_PROMPT,
529 #[cfg(feature = "linux-pam-ext")]
447 /// App-supplied function to override failure delays. 530 /// App-supplied function to override failure delays.
448 FailDelay = 10, 531 FailDelay = constants::PAM_FAIL_DELAY,
532 #[cfg(feature = "linux-pam-ext")]
449 /// X display name. 533 /// X display name.
450 XDisplay = 11, 534 XDisplay = constants::PAM_XDISPLAY,
535 #[cfg(feature = "linux-pam-ext")]
451 /// X server authentication data. 536 /// X server authentication data.
452 XAuthData = 12, 537 XAuthData = constants::PAM_XAUTHDATA,
538 #[cfg(feature = "linux-pam-ext")]
453 /// The type of `pam_get_authtok`. 539 /// The type of `pam_get_authtok`.
454 AuthTokType = 13, 540 AuthTokType = constants::PAM_AUTHTOK_TYPE,
455 } 541 }