comparison src/libpam/handle.rs @ 75:c30811b4afae

rename pam_ffi submodule to libpam.
author Paul Fisher <paul@pfish.zone>
date Fri, 06 Jun 2025 22:35:08 -0400
parents src/pam_ffi/handle.rs@c7c596e6388f
children 351bdc13005e
comparison
equal deleted inserted replaced
74:c7c596e6388f 75:c30811b4afae
1 use super::conversation::LibPamConversation;
2 use crate::constants::{ErrorCode, InvalidEnum, Result};
3 use crate::conv::Message;
4 use crate::handle::{PamApplicationOnly, PamModuleOnly, PamShared};
5 use crate::libpam::memory;
6 use crate::libpam::memory::Immovable;
7 use crate::{Conversation, Response};
8 use num_derive::FromPrimitive;
9 use num_traits::FromPrimitive;
10 use std::ffi::{c_char, c_int};
11 use std::ops::{Deref, DerefMut};
12 use std::result::Result as StdResult;
13 use std::{mem, ptr};
14
15 /// An owned PAM handle.
16 #[repr(transparent)]
17 pub struct OwnedLibPamHandle(*mut LibPamHandle);
18
19 /// An opaque structure that a PAM handle points to.
20 #[repr(C)]
21 pub struct LibPamHandle {
22 _data: (),
23 _marker: Immovable,
24 }
25
26 impl LibPamHandle {
27 /// Gets a C string item.
28 ///
29 /// # Safety
30 ///
31 /// You better be requesting an item which is a C string.
32 unsafe fn get_cstr_item(&mut self, item_type: ItemType) -> Result<Option<&str>> {
33 let mut output = ptr::null();
34 let ret = unsafe { super::pam_get_item(self, item_type as c_int, &mut output) };
35 ErrorCode::result_from(ret)?;
36 memory::wrap_string(output.cast())
37 }
38
39 /// Sets a C string item.
40 ///
41 /// # Safety
42 ///
43 /// You better be setting an item which is a C string.
44 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> {
45 let data_str = memory::option_cstr(data)?;
46 let ret = unsafe {
47 super::pam_set_item(
48 self,
49 item_type as c_int,
50 memory::prompt_ptr(data_str.as_ref()).cast(),
51 )
52 };
53 ErrorCode::result_from(ret)
54 }
55
56 /// Gets the `PAM_CONV` item from the handle.
57 fn conversation_item(&mut self) -> Result<&mut LibPamConversation> {
58 let output: *mut LibPamConversation = ptr::null_mut();
59 let result = unsafe {
60 super::pam_get_item(
61 self,
62 ItemType::Conversation.into(),
63 &mut output.cast_const().cast(),
64 )
65 };
66 ErrorCode::result_from(result)?;
67 // SAFETY: We got this result from PAM, and we're checking if it's null.
68 unsafe { output.as_mut() }.ok_or(ErrorCode::ConversationError)
69 }
70 }
71
72 impl PamApplicationOnly for OwnedLibPamHandle {
73 fn close(self, status: Result<()>) -> Result<()> {
74 let ret = unsafe { super::pam_end(self.0, ErrorCode::result_to_c(status)) };
75 // Forget rather than dropping, since dropping also calls pam_end.
76 mem::forget(self);
77 ErrorCode::result_from(ret)
78 }
79 }
80
81 impl Deref for OwnedLibPamHandle {
82 type Target = LibPamHandle;
83 fn deref(&self) -> &Self::Target {
84 unsafe { &*self.0 }
85 }
86 }
87
88 impl DerefMut for OwnedLibPamHandle {
89 fn deref_mut(&mut self) -> &mut Self::Target {
90 unsafe { &mut *self.0 }
91 }
92 }
93
94 impl Drop for OwnedLibPamHandle {
95 /// Ends the PAM session with a zero error code.
96 /// You probably want to call [`close`](Self::close) instead of
97 /// letting this drop by itself.
98 fn drop(&mut self) {
99 unsafe {
100 super::pam_end(self.0, 0);
101 }
102 }
103 }
104
105 macro_rules! cstr_item {
106 (get = $getter:ident, item = $item_type:path) => {
107 fn $getter(&mut self) -> Result<Option<&str>> {
108 unsafe { self.get_cstr_item($item_type) }
109 }
110 };
111 (set = $setter:ident, item = $item_type:path) => {
112 fn $setter(&mut self, value: Option<&str>) -> Result<()> {
113 unsafe { self.set_cstr_item($item_type, value) }
114 }
115 };
116 }
117
118 impl PamShared for LibPamHandle {
119 fn get_user(&mut self, prompt: Option<&str>) -> Result<&str> {
120 let prompt = memory::option_cstr(prompt)?;
121 let mut output: *const c_char = ptr::null();
122 let ret =
123 unsafe { super::pam_get_user(self, &mut output, memory::prompt_ptr(prompt.as_ref())) };
124 ErrorCode::result_from(ret)?;
125 unsafe { memory::wrap_string(output) }
126 .transpose()
127 .unwrap_or(Err(ErrorCode::ConversationError))
128 }
129
130 cstr_item!(get = user_item, item = ItemType::User);
131 cstr_item!(set = set_user_item, item = ItemType::User);
132 cstr_item!(get = service, item = ItemType::Service);
133 cstr_item!(set = set_service, item = ItemType::Service);
134 cstr_item!(get = user_prompt, item = ItemType::UserPrompt);
135 cstr_item!(set = set_user_prompt, item = ItemType::UserPrompt);
136 cstr_item!(get = tty_name, item = ItemType::Tty);
137 cstr_item!(set = set_tty_name, item = ItemType::Tty);
138 cstr_item!(get = remote_user, item = ItemType::RemoteUser);
139 cstr_item!(set = set_remote_user, item = ItemType::RemoteUser);
140 cstr_item!(get = remote_host, item = ItemType::RemoteHost);
141 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost);
142 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok);
143 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok);
144 }
145
146 impl Conversation for LibPamHandle {
147 fn communicate(&mut self, messages: &[Message]) -> Result<Vec<Response>> {
148 self.conversation_item()?.communicate(messages)
149 }
150 }
151
152 impl PamModuleOnly for LibPamHandle {
153 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<&str> {
154 let prompt = memory::option_cstr(prompt)?;
155 let mut output: *const c_char = ptr::null_mut();
156 // SAFETY: We're calling this with known-good values.
157 let res = unsafe {
158 super::pam_get_authtok(
159 self,
160 ItemType::AuthTok.into(),
161 &mut output,
162 memory::prompt_ptr(prompt.as_ref()),
163 )
164 };
165 ErrorCode::result_from(res)?;
166 // SAFETY: We got this string from PAM.
167 unsafe { memory::wrap_string(output) }
168 .transpose()
169 .unwrap_or(Err(ErrorCode::ConversationError))
170 }
171
172 cstr_item!(get = authtok_item, item = ItemType::AuthTok);
173 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok);
174 }
175
176 /// Function called at the end of a PAM session that is called to clean up
177 /// a value previously provided to PAM in a `pam_set_data` call.
178 ///
179 /// You should never call this yourself.
180 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) {
181 unsafe {
182 let _data: Box<T> = Box::from_raw(c_data.cast());
183 }
184 }
185
186 /// Identifies what is being gotten or set with `pam_get_item`
187 /// or `pam_set_item`.
188 #[derive(FromPrimitive)]
189 #[repr(i32)]
190 #[non_exhaustive] // because C could give us anything!
191 pub enum ItemType {
192 /// The PAM service name.
193 Service = 1,
194 /// The user's login name.
195 User = 2,
196 /// The TTY name.
197 Tty = 3,
198 /// The remote host (if applicable).
199 RemoteHost = 4,
200 /// The conversation struct (not a CStr-based item).
201 Conversation = 5,
202 /// The authentication token (password).
203 AuthTok = 6,
204 /// The old authentication token (when changing passwords).
205 OldAuthTok = 7,
206 /// The remote user's name.
207 RemoteUser = 8,
208 /// The prompt shown when requesting a username.
209 UserPrompt = 9,
210 /// App-supplied function to override failure delays.
211 FailDelay = 10,
212 /// X display name.
213 XDisplay = 11,
214 /// X server authentication data.
215 XAuthData = 12,
216 /// The type of `pam_get_authtok`.
217 AuthTokType = 13,
218 }
219
220 impl TryFrom<c_int> for ItemType {
221 type Error = InvalidEnum<Self>;
222 fn try_from(value: c_int) -> StdResult<Self, Self::Error> {
223 Self::from_i32(value).ok_or(value.into())
224 }
225 }
226
227 impl From<ItemType> for c_int {
228 fn from(val: ItemType) -> Self {
229 val as Self
230 }
231 }