Mercurial > crates > nonstick
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 } |