comparison src/handle.rs @ 64:bbe84835d6db v0.0.5

More organization; add lots of docs. - moves `PamHandle` to its own module, since it will be used by both modules and clients. - adds a ton of documentation to the `PamModule` trait and reorders methods to most-interesting-first. - adds more flag values from pam_modules.h. - other misc cleanup.
author Paul Fisher <paul@pfish.zone>
date Thu, 22 May 2025 01:52:32 -0400
parents src/module.rs@05cc2c27334f
children
comparison
equal deleted inserted replaced
63:a7aa5ca0d00d 64:bbe84835d6db
1 //! Where [PamHandle] lives.
2 use crate::items::{Item, ItemType};
3 use crate::{memory, pam_ffi, ErrorCode};
4 use libc::c_char;
5 use secure_string::SecureString;
6 use std::ffi::{c_int, CString};
7
8 /// Your interface to a PAM handle.
9 ///
10 /// This structure wraps an opaque PAM-provided pointer and gives you
11 /// a safe and familiar struct-based API to interact with PAM.
12 #[repr(transparent)]
13 pub struct PamHandle(*mut libc::c_void);
14
15 impl PamHandle {
16 /// Retrieves the name of the user who is authenticating or logging in.
17 ///
18 /// This is effectively like `handle.get_item::<Item::User>()`.
19 /// See the [`pam_get_user` manual page][man]
20 /// or [`pam_get_user` in the Module Writer's Guide][mwg].
21 ///
22 /// # Example
23 ///
24 /// ```no_run
25 /// # use nonstick::PamHandle;
26 /// # fn _doc(handle: &PamHandle) -> Result<(), Box<dyn std::error::Error>> {
27 /// // Get the username using the default prompt.
28 /// let user = handle.get_user(None)?;
29 /// // Get the username using a custom prompt.
30 /// let user = handle.get_user(Some("who ARE you even???"))?;
31 /// # Ok(())
32 /// # }
33 /// ```
34 ///
35 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html
36 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user
37 pub fn get_user(&self, prompt: Option<&str>) -> crate::Result<String> {
38 let prompt = memory::option_cstr(prompt)?;
39 let mut output: *const c_char = std::ptr::null_mut();
40 let ret = unsafe {
41 pam_ffi::pam_get_user(self.0, &mut output, memory::prompt_ptr(prompt.as_ref()))
42 };
43 ErrorCode::result_from(ret)?;
44 memory::copy_pam_string(output)
45 }
46
47 /// Retrieves the authentication token from the user.
48 ///
49 /// This is essentially like `handle.get_item::<Item::AuthTok>()`.
50 ///
51 /// See the [`pam_get_authtok` manual page][man]
52 /// or [`pam_get_item` in the Module Writer's Guide][mwg].
53 ///
54 /// # Example
55 ///
56 /// ```no_run
57 /// # use nonstick::PamHandle;
58 /// # fn _doc(handle: &PamHandle) -> Result<(), Box<dyn std::error::Error>> {
59 /// // Get the user's password using the default prompt.
60 /// let pass = handle.get_authtok(None)?;
61 /// // Get the user's password using a custom prompt.
62 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?;
63 /// Ok(())
64 /// # }
65 /// ```
66 ///
67 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html
68 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item
69 pub fn get_authtok(&self, prompt: Option<&str>) -> crate::Result<SecureString> {
70 let prompt = memory::option_cstr(prompt)?;
71 let mut output: *const c_char = std::ptr::null_mut();
72 let res = unsafe {
73 pam_ffi::pam_get_authtok(
74 self.0,
75 ItemType::AuthTok.into(),
76 &mut output,
77 memory::prompt_ptr(prompt.as_ref()),
78 )
79 };
80 ErrorCode::result_from(res)?;
81 memory::copy_pam_string(output).map(SecureString::from)
82 }
83
84 /// Retrieves an [Item] that has been set, possibly by the PAM client.
85 ///
86 /// These items are *references to PAM memory*
87 /// which are *owned by the PAM session*
88 /// and you should never modify them.
89 ///
90 /// See the [`pam_get_item` manual page][man]
91 /// or [`pam_get_item` in the Module Writer's Guide][mwg].
92 ///
93 /// # Example
94 ///
95 /// ```no_run
96 /// # use nonstick::PamHandle;
97 /// use nonstick::items::Service;
98 ///
99 /// # fn _doc(pam_handle: &PamHandle) -> Result<(), Box<dyn std::error::Error>> {
100 /// let svc: Option<Service> = pam_handle.get_item()?;
101 /// match svc {
102 /// Some(name) => eprintln!("The calling service name is {:?}", name.to_string_lossy()),
103 /// None => eprintln!("Who knows what the calling service is?"),
104 /// }
105 /// # Ok(())
106 /// # }
107 /// ```
108 ///
109 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html
110 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item
111 pub fn get_item<T: Item>(&self) -> crate::Result<Option<T>> {
112 let mut ptr: *const libc::c_void = std::ptr::null();
113 let out = unsafe {
114 let ret = pam_ffi::pam_get_item(self.0, T::type_id().into(), &mut ptr);
115 ErrorCode::result_from(ret)?;
116 let typed_ptr: *const T::Raw = ptr.cast();
117 match typed_ptr.is_null() {
118 true => None,
119 false => Some(T::from_raw(typed_ptr)),
120 }
121 };
122 Ok(out)
123 }
124
125 /// Sets an item in the PAM context. It can be retrieved using [`get_item`](Self::get_item).
126 ///
127 /// See the [`pam_set_item` manual page][man]
128 /// or [`pam_set_item` in the Module Writer's Guide][mwg].
129 ///
130 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html
131 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item
132 pub fn set_item<T: Item>(&mut self, item: T) -> crate::Result<()> {
133 let ret =
134 unsafe { pam_ffi::pam_set_item(self.0, T::type_id().into(), item.into_raw().cast()) };
135 ErrorCode::result_from(ret)
136 }
137
138 /// Gets some pointer, identified by `key`, that has been set previously
139 /// using [`set_data`](Self::set_data).
140 ///
141 /// The data, if present, is still owned by the current PAM session.
142 ///
143 /// See the [`pam_get_data` manual page][man]
144 /// or [`pam_get_data` in the Module Writer's Guide][mwg].
145 ///
146 /// # Safety
147 ///
148 /// The data stored under the provided key must be of type `T`,
149 /// otherwise you'll get back a completely invalid `&T`
150 /// and further behavior is undefined.
151 ///
152 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html
153 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data
154 pub unsafe fn get_data<T>(&self, key: &str) -> crate::Result<Option<&T>> {
155 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?;
156 let mut ptr: *const libc::c_void = std::ptr::null();
157 ErrorCode::result_from(pam_ffi::pam_get_data(self.0, c_key.as_ptr(), &mut ptr))?;
158 match ptr.is_null() {
159 true => Ok(None),
160 false => {
161 let typed_ptr = ptr.cast();
162 Ok(Some(&*typed_ptr))
163 }
164 }
165 }
166
167 /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data).
168 ///
169 /// This data is accessible to this module and other PAM modules
170 /// (using the provided `key`), but is *not* accessible to the application.
171 /// The PAM session takes ownership of the data, and it will be dropped
172 /// when the session ends.
173 ///
174 /// See the [`pam_set_data` manual page][man]
175 /// or [`pam_set_data` in the Module Writer's Guide][mwg].
176 ///
177 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html
178 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_data
179 pub fn set_data<T>(&mut self, key: &str, data: Box<T>) -> crate::Result<()> {
180 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?;
181 let ret = unsafe {
182 pam_ffi::pam_set_data(
183 self.0,
184 c_key.as_ptr(),
185 Box::into_raw(data).cast(),
186 Self::set_data_cleanup::<T>,
187 )
188 };
189 ErrorCode::result_from(ret)
190 }
191
192 /// Function called at the end of a PAM session that is called to clean up
193 /// a value previously provided to PAM in a `pam_set_data` call.
194 ///
195 /// You should never call this yourself.
196 extern "C" fn set_data_cleanup<T>(_: *const libc::c_void, c_data: *mut libc::c_void, _: c_int) {
197 unsafe {
198 let _data: Box<T> = Box::from_raw(c_data.cast());
199 }
200 }
201 }
202
203 impl From<*mut libc::c_void> for PamHandle {
204 /// Wraps an internal Handle pointer.
205 fn from(value: *mut libc::c_void) -> Self {
206 Self(value)
207 }
208 }