Mercurial > crates > nonstick
comparison src/handle.rs @ 72:47eb242a4f88
Fill out the PamHandle trait.
This updates the PamHandle trait to have methods for each Item,
and implements them on the LibPamHandle.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Wed, 04 Jun 2025 03:53:36 -0400 |
parents | 58f9d2a4df38 |
children | ac6881304c78 |
comparison
equal
deleted
inserted
replaced
71:58f9d2a4df38 | 72:47eb242a4f88 |
---|---|
1 //! The wrapper types and traits for handles into the PAM library. | 1 //! The wrapper types and traits for handles into the PAM library. |
2 use crate::constants::{ErrorCode, Result}; | 2 use crate::constants::{ErrorCode, Result}; |
3 use crate::items::{Item, ItemType}; | 3 use crate::conv::Conversation; |
4 use crate::items::ItemType; | |
5 use crate::module::ConversationMux; | |
4 use crate::pam_ffi; | 6 use crate::pam_ffi; |
5 use crate::pam_ffi::memory; | 7 use crate::pam_ffi::{memory, LibPamConversation, LibPamHandle}; |
6 use secure_string::SecureString; | 8 use std::ffi::{c_char, c_int}; |
7 use std::ffi::{c_char, c_int, c_void, CString}; | |
8 use std::{mem, ptr}; | 9 use std::{mem, ptr}; |
10 | |
11 macro_rules! trait_item { | |
12 (get = $getter:ident, item = $item:literal $(, see = $see:path)? $(, $($doc:literal)*)?) => { | |
13 $( | |
14 $(#[doc = $doc])* | |
15 #[doc = ""] | |
16 )? | |
17 #[doc = concat!("Gets the `", $item, "` of the PAM handle.")] | |
18 $( | |
19 #[doc = concat!("See [`", stringify!($see), "`].")] | |
20 )? | |
21 #[doc = ""] | |
22 #[doc = "Returns a reference to the item's value, owned by PAM."] | |
23 #[doc = "The item is assumed to be valid UTF-8 text."] | |
24 #[doc = "If it is not, `ConversationError` is returned."] | |
25 #[doc = ""] | |
26 #[doc = "See the [`pam_get_item`][man] manual page,"] | |
27 #[doc = "[`pam_get_item` in the Module Writers' Guide][mwg], or"] | |
28 #[doc = "[`pam_get_item` in the Application Developers' Guide][adg]."] | |
29 #[doc = ""] | |
30 #[doc = "[man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html"] | |
31 #[doc = "[adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_get_item"] | |
32 #[doc = "[mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item"] | |
33 fn $getter(&mut self) -> Result<Option<&str>>; | |
34 }; | |
35 (set = $setter:ident, item = $item:literal $(, see = $see:path)? $(, $($doc:literal)*)?) => { | |
36 $( | |
37 $(#[doc = $doc])* | |
38 #[doc = ""] | |
39 )? | |
40 #[doc = concat!("Sets the `", $item, "` from the PAM handle.")] | |
41 $( | |
42 #[doc = concat!("See [`", stringify!($see), "`].")] | |
43 )? | |
44 #[doc = ""] | |
45 #[doc = "Sets the item's value. PAM copies the string's contents."] | |
46 #[doc = "If the string contains a null byte, this will return "] | |
47 #[doc = "a `ConversationError`."] | |
48 #[doc = ""] | |
49 #[doc = "See the [`pam_set_item`][man] manual page,"] | |
50 #[doc = "[`pam_set_item` in the Module Writers' Guide][mwg], or"] | |
51 #[doc = "[`pam_set_item` in the Application Developers' Guide][adg]."] | |
52 #[doc = ""] | |
53 #[doc = "[man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html"] | |
54 #[doc = "[adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_set_item"] | |
55 #[doc = "[mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item"] | |
56 fn $setter(&mut self, value: Option<&str>) -> Result<()>; | |
57 }; | |
58 } | |
9 | 59 |
10 /// Features of a PAM handle that are available to applications and modules. | 60 /// Features of a PAM handle that are available to applications and modules. |
11 /// | 61 /// |
12 /// You probably want [`LibPamHandle`]. This trait is intended to allow creating | 62 /// You probably want [`LibPamHandle`]. This trait is intended to allow creating |
13 /// mock PAM handle types used for testing PAM modules and applications. | 63 /// mock PAM handle types used for testing PAM modules and applications. |
14 pub trait PamHandle { | 64 pub trait PamHandle { |
65 type Conv: Conversation; | |
15 /// Retrieves the name of the user who is authenticating or logging in. | 66 /// Retrieves the name of the user who is authenticating or logging in. |
16 /// | 67 /// |
17 /// This is effectively like `handle.get_item::<Item::User>()`. | 68 /// If the username has previously been obtained, this uses that username; |
69 /// otherwise it prompts the user with the first of these that is present: | |
70 /// | |
71 /// 1. The prompt string passed to this function. | |
72 /// 2. The string returned by `get_user_prompt_item`. | |
73 /// 3. The default prompt, `login: ` | |
74 /// | |
18 /// See the [`pam_get_user` manual page][man] | 75 /// See the [`pam_get_user` manual page][man] |
19 /// or [`pam_get_user` in the Module Writer's Guide][mwg]. | 76 /// or [`pam_get_user` in the Module Writer's Guide][mwg]. |
20 /// | 77 /// |
21 /// # Example | 78 /// # Example |
22 /// | 79 /// |
23 /// ```no_run | 80 /// ```no_run |
24 /// # use nonstick::PamHandle; | 81 /// # use nonstick::PamModuleHandle; |
25 /// # fn _doc(handle: &mut impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { | 82 /// # fn _doc(handle: &mut impl PamModuleHandle) -> Result<(), Box<dyn std::error::Error>> { |
26 /// // Get the username using the default prompt. | 83 /// // Get the username using the default prompt. |
27 /// let user = handle.get_user(None)?; | 84 /// let user = handle.get_user(None)?; |
28 /// // Get the username using a custom prompt. | 85 /// // Get the username using a custom prompt. |
29 /// let user = handle.get_user(Some("who ARE you even???"))?; | 86 /// // If this were actually called right after the above, |
87 /// // both user and user_2 would have the same value. | |
88 /// let user_2 = handle.get_user(Some("who ARE you even???"))?; | |
30 /// # Ok(()) | 89 /// # Ok(()) |
31 /// # } | 90 /// # } |
32 /// ``` | 91 /// ``` |
33 /// | 92 /// |
34 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html | 93 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html |
35 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user | 94 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user |
36 fn get_user(&mut self, prompt: Option<&str>) -> Result<String>; | 95 fn get_user(&mut self, prompt: Option<&str>) -> Result<Option<&str>>; |
37 | 96 |
38 /// Retrieves the authentication token from the user. | 97 trait_item!( |
39 /// | 98 get = user_item, |
40 /// This is essentially like `handle.get_item::<Item::AuthTok>()`. | 99 item = "PAM_USER", |
41 /// | 100 "The identity of the user for whom service is being requested." |
42 /// See the [`pam_get_authtok` manual page][man] | 101 "" |
43 /// or [`pam_get_item` in the Module Writer's Guide][mwg]. | 102 "While PAM usually sets this automatically during the course of " |
44 /// | 103 "a [`get_user`](Self::get_user) call, it may be changed by a module " |
45 /// # Example | 104 "over the course of the PAM transaction." |
46 /// | 105 "Applications should check it after each step of the PAM process." |
47 /// ```no_run | 106 ); |
48 /// # use nonstick::PamHandle; | 107 trait_item!( |
49 /// # fn _doc(handle: &mut impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { | 108 set = set_user_item, |
50 /// // Get the user's password using the default prompt. | 109 item = "PAM_USER", |
51 /// let pass = handle.get_authtok(None)?; | 110 see = Self::user_item, |
52 /// // Get the user's password using a custom prompt. | 111 "Sets the identity of the logging-in user." |
53 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; | 112 "" |
54 /// Ok(()) | 113 "Usually this will be set during the course of " |
55 /// # } | 114 "a [`get_user`](Self::get_user) call, but you may set it manually " |
56 /// ``` | 115 "or change it during the PAM process." |
57 /// | 116 ); |
58 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html | 117 |
59 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item | 118 trait_item!( |
60 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<SecureString>; | 119 get = service, |
61 | 120 item = "PAM_SERVICE", |
62 /// Retrieves an [Item] that has been set, possibly by the PAM client. | 121 "The service name, which identifies the PAM stack which is used " |
63 /// | 122 "to perform authentication." |
64 /// These items are *references to PAM memory* | 123 ); |
65 /// which are *owned by the PAM session* | 124 trait_item!( |
66 /// and you should never modify them. | 125 set = set_service, |
67 /// | 126 item = "PAM_SERVICE", |
68 /// See the [`pam_get_item` manual page][man] | 127 see = Self::service, |
69 /// or [`pam_get_item` in the Module Writer's Guide][mwg]. | 128 "The service name, which identifies the PAM stack which is used " |
70 /// | 129 "to perform authentication. It's probably a bad idea to change this." |
71 /// # Example | 130 ); |
72 /// | 131 |
73 /// ```no_run | 132 trait_item!( |
74 /// # use nonstick::PamHandle; | 133 get = user_prompt, |
75 /// use nonstick::items::Service; | 134 item = "PAM_USER_PROMPT", |
76 /// | 135 "The string used to prompt for a user's name." |
77 /// # fn _doc(pam_handle: &mut impl PamHandle) -> Result<(), Box<dyn std::error::Error>> { | 136 "By default, this is a localized version of `login: `." |
78 /// let svc: Option<Service> = pam_handle.get_item()?; | 137 ); |
79 /// match svc { | 138 trait_item!( |
80 /// Some(name) => eprintln!("The calling service name is {:?}", name.to_string_lossy()), | 139 set = set_user_prompt, |
81 /// None => eprintln!("Who knows what the calling service is?"), | 140 item = "PAM_USER_PROMPT", |
82 /// } | 141 see = Self::user_prompt, |
83 /// # Ok(()) | 142 "Sets the string used to prompt for a user's name." |
84 /// # } | 143 ); |
85 /// ``` | 144 |
86 /// | 145 trait_item!( |
87 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html | 146 get = tty_name, |
88 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item | 147 item = "PAM_TTY", |
89 fn get_item<T: Item>(&mut self) -> Result<Option<T>>; | 148 "\"The terminal name prefixed by /dev/ for device files.\"" |
90 | 149 "" |
91 /// Sets an item in the PAM context. It can be retrieved using [`get_item`](Self::get_item). | 150 "This is the terminal the user is logging in on." |
92 /// | 151 "Very old applications may use this instead of `PAM_XDISPLAY`." |
93 /// See the [`pam_set_item` manual page][man] | 152 ); |
94 /// or [`pam_set_item` in the Module Writer's Guide][mwg]. | 153 trait_item!( |
95 /// | 154 set = set_tty_name, |
96 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html | 155 item = "PAM_TTY", |
97 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item | 156 see = Self::tty_name, |
98 fn set_item<T: Item>(&mut self, item: T) -> Result<()>; | 157 "Sets the terminal name." |
158 "" | |
159 "(TODO: See if libpam sets this itself or if the application does.)" | |
160 ); | |
161 | |
162 trait_item!( | |
163 get = remote_user, | |
164 item = "PAM_RUSER", | |
165 "If set, the identity of the remote user logging in." | |
166 "" | |
167 "This is only as trustworthy as the application calling PAM." | |
168 "Also see [`remote_host`](Self::remote_host)." | |
169 ); | |
170 trait_item!( | |
171 set = set_remote_user, | |
172 item = "PAM_RUSER", | |
173 "Sets the identity of the remote user logging in." | |
174 "" | |
175 "This is usually set by the application before making calls " | |
176 "into a PAM session. (TODO: check this!)" | |
177 ); | |
178 | |
179 trait_item!( | |
180 get = remote_host, | |
181 item = "PAM_RHOST", | |
182 "If set, the remote location where the user is coming from." | |
183 "" | |
184 "This is only as trustworthy as the application calling PAM. " | |
185 "This can be combined with [`Self::remote_user`] to identify " | |
186 "the account the user is attempting to log in from, " | |
187 "with `remote_user@remote_host`." | |
188 "" | |
189 "If unset, \"it is unclear where the authentication request " | |
190 "is originating from.\"" | |
191 ); | |
192 trait_item!( | |
193 set = set_remote_host, | |
194 item = "PAM_RHOST", | |
195 see = Self::remote_host, | |
196 "Sets the location where the user is coming from." | |
197 "" | |
198 "This is usually set by the application before making calls " | |
199 "into a PAM session. (TODO: check this!)" | |
200 ); | |
201 | |
202 trait_item!( | |
203 set = set_authtok_item, | |
204 item = "PAM_AUTHTOK", | |
205 see = PamModuleHandle::authtok_item, | |
206 "Sets the user's authentication token (e.g., password)." | |
207 "" | |
208 "This is usually set automatically when " | |
209 "[`get_authtok`](PamModuleHandle::get_authtok) is called, " | |
210 "but can be manually set." | |
211 ); | |
212 | |
213 trait_item!( | |
214 set = set_old_authtok_item, | |
215 item = "PAM_OLDAUTHTOK", | |
216 see = PamModuleHandle::old_authtok_item, | |
217 "Sets the user's \"old authentication token\" when changing passwords." | |
218 "" | |
219 "This is usually set automatically by PAM." | |
220 ); | |
99 } | 221 } |
100 | 222 |
101 /// Functionality of a PAM handle that can be expected by a PAM application. | 223 /// Functionality of a PAM handle that can be expected by a PAM application. |
102 /// | 224 /// |
103 /// If you are not writing a PAM client application (e.g., you are writing | 225 /// If you are not writing a PAM client application (e.g., you are writing |
126 /// # } | 248 /// # } |
127 /// ``` | 249 /// ``` |
128 /// | 250 /// |
129 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_end.3.html | 251 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_end.3.html |
130 fn close(self, status: Result<()>) -> Result<()>; | 252 fn close(self, status: Result<()>) -> Result<()>; |
253 | |
254 /// Uses a new PAM conversation. | |
255 fn set_conversation(&mut self, conversation: Self::Conv) -> Result<()>; | |
131 } | 256 } |
132 | 257 |
133 /// Functionality of a PAM handle that can be expected by a PAM module. | 258 /// Functionality of a PAM handle that can be expected by a PAM module. |
134 /// | 259 /// |
135 /// If you are not writing a PAM module (e.g., you are writing an application), | 260 /// If you are not writing a PAM module (e.g., you are writing an application), |
136 /// you should not use any of the functionality exposed by this trait. | 261 /// you should not use any of the functionality exposed by this trait. |
137 /// | 262 /// |
138 /// Like [`PamHandle`], this is intended to allow creating mock implementations | 263 /// Like [`PamHandle`], this is intended to allow creating mock implementations |
139 /// of PAM for testing PAM modules. | 264 /// of PAM for testing PAM modules. |
140 pub trait PamModuleHandle: PamHandle { | 265 pub trait PamModuleHandle: PamHandle { |
141 /// Gets some pointer, identified by `key`, that has been set previously | 266 /// Gets a channel for communication with the user. |
142 /// using [`set_data`](Self::set_data). | 267 /// |
143 /// | 268 /// The Conversation is the conduit which you use for all communication |
144 /// The data, if present, is still owned by the current PAM session. | 269 /// with the user. |
145 /// | 270 fn conversation(&mut self) -> Result<ConversationMux<'_, Self::Conv>>; |
146 /// See the [`pam_get_data` manual page][man] | 271 |
147 /// or [`pam_get_data` in the Module Writer's Guide][mwg]. | 272 /// Retrieves the authentication token from the user. |
273 /// | |
274 /// This should only be used by *authentication* and *password-change* | |
275 /// PAM modules. | |
276 /// | |
277 /// See the [`pam_get_authtok` manual page][man] | |
278 /// or [`pam_get_item` in the Module Writer's Guide][mwg]. | |
279 /// | |
280 /// # Example | |
281 /// | |
282 /// ```no_run | |
283 /// # use nonstick::PamModuleHandle; | |
284 /// # fn _doc(handle: &mut impl PamModuleHandle) -> Result<(), Box<dyn std::error::Error>> { | |
285 /// // Get the user's password using the default prompt. | |
286 /// let pass = handle.get_authtok(None)?; | |
287 /// // Get the user's password using a custom prompt. | |
288 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; | |
289 /// Ok(()) | |
290 /// # } | |
291 /// ``` | |
292 /// | |
293 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html | |
294 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item | |
295 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<Option<&str>>; | |
296 | |
297 trait_item!( | |
298 get = authtok_item, | |
299 item = "PAM_AUTHTOK", | |
300 see = Self::get_authtok, | |
301 "Gets the user's authentication token (e.g., password)." | |
302 "" | |
303 "This is normally set automatically by PAM when calling " | |
304 "[`get_authtok`](Self::get_authtok), but can be set explicitly." | |
305 "" | |
306 "Like `get_authtok`, this should only ever be called " | |
307 "by *authentication* and *password-change* PAM modules." | |
308 ); | |
309 | |
310 trait_item!( | |
311 get = old_authtok_item, | |
312 item = "PAM_OLDAUTHTOK", | |
313 see = PamHandle::set_old_authtok_item, | |
314 "Gets the user's old authentication token when changing passwords." | |
315 "" | |
316 "This should only ever be called by *password-change* PAM modules." | |
317 ); | |
318 | |
319 /* | |
320 TODO: Re-enable this at some point. | |
321 /// Gets some pointer, identified by `key`, that has been set previously | |
322 /// using [`set_data`](Self::set_data). | |
323 /// | |
324 /// The data, if present, is still owned by the current PAM session. | |
325 /// | |
326 /// See the [`pam_get_data` manual page][man] | |
327 /// or [`pam_get_data` in the Module Writer's Guide][mwg]. | |
328 /// | |
329 /// # Safety | |
330 /// | |
331 /// The data stored under the provided key must be of type `T`, | |
332 /// otherwise you'll get back a completely invalid `&T` | |
333 /// and further behavior is undefined. | |
334 /// | |
335 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html | |
336 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data | |
337 unsafe fn get_data<T>(&mut self, key: &str) -> Result<Option<&T>>; | |
338 | |
339 /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data). | |
340 /// | |
341 /// This data is accessible to this module and other PAM modules | |
342 /// (using the provided `key`), but is *not* accessible to the application. | |
343 /// The PAM session takes ownership of the data, and it will be dropped | |
344 /// when the session ends. | |
345 /// | |
346 /// See the [`pam_set_data` manual page][man] | |
347 /// or [`pam_set_data` in the Module Writer's Guide][mwg]. | |
348 /// | |
349 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html | |
350 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_data | |
351 fn set_data<T>(&mut self, key: &str, data: Box<T>) -> Result<()>; | |
352 */ | |
353 } | |
354 | |
355 | |
356 impl LibPamHandle { | |
357 /// Gets a C string item. | |
148 /// | 358 /// |
149 /// # Safety | 359 /// # Safety |
150 /// | 360 /// |
151 /// The data stored under the provided key must be of type `T`, | 361 /// You better be requesting an item which is a C string. |
152 /// otherwise you'll get back a completely invalid `&T` | 362 unsafe fn get_cstr_item(&mut self, item_type: ItemType) -> Result<Option<&str>> { |
153 /// and further behavior is undefined. | 363 let mut output = ptr::null(); |
154 /// | 364 let ret = unsafe { pam_ffi::pam_get_item(self, item_type as c_int, &mut output) }; |
155 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html | 365 ErrorCode::result_from(ret)?; |
156 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data | 366 memory::wrap_string(output.cast()) |
157 unsafe fn get_data<T>(&mut self, key: &str) -> Result<Option<&T>>; | 367 } |
158 | 368 |
159 /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data). | 369 /// Sets a C string item. |
160 /// | 370 /// |
161 /// This data is accessible to this module and other PAM modules | 371 /// # Safety |
162 /// (using the provided `key`), but is *not* accessible to the application. | 372 /// |
163 /// The PAM session takes ownership of the data, and it will be dropped | 373 /// You better be setting an item which is a C string. |
164 /// when the session ends. | 374 unsafe fn set_cstr_item(&mut self, item_type: ItemType, data: Option<&str>) -> Result<()> { |
165 /// | 375 let data_str = memory::option_cstr(data)?; |
166 /// See the [`pam_set_data` manual page][man] | 376 let ret = unsafe { |
167 /// or [`pam_set_data` in the Module Writer's Guide][mwg]. | 377 pam_ffi::pam_set_item( |
168 /// | 378 self, |
169 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html | 379 item_type as c_int, |
170 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_data | 380 memory::prompt_ptr(data_str.as_ref()).cast(), |
171 fn set_data<T>(&mut self, key: &str, data: Box<T>) -> Result<()>; | 381 ) |
172 } | 382 }; |
173 | 383 ErrorCode::result_from(ret) |
174 /// A [`PamHandle`] backed by `libpam`, i.e., a real PAM handle. | 384 } |
175 /// | 385 } |
176 /// This structure wraps an opaque PAM handle and gives you a nice Rusty | |
177 /// interface to use PAM. | |
178 #[repr(C)] | |
179 pub struct LibPamHandle(pam_ffi::Handle); | |
180 | 386 |
181 impl Drop for LibPamHandle { | 387 impl Drop for LibPamHandle { |
182 /// Ends the PAM session with a zero error code. | 388 /// Ends the PAM session with a zero error code. |
183 /// You probably want to call [`close`](Self::close) instead of | 389 /// You probably want to call [`close`](Self::close) instead of |
184 /// letting this drop by itself. | 390 /// letting this drop by itself. |
185 fn drop(&mut self) { | 391 fn drop(&mut self) { |
186 unsafe { | 392 unsafe { |
187 pam_ffi::pam_end(&mut self.0, 0); | 393 pam_ffi::pam_end(self, 0); |
188 } | 394 } |
189 } | 395 } |
190 } | 396 } |
191 | 397 |
398 macro_rules! cstr_item { | |
399 (get = $getter:ident, item = $item_type:path) => { | |
400 fn $getter(&mut self) -> Result<Option<&str>> { | |
401 unsafe { self.get_cstr_item($item_type) } | |
402 } | |
403 }; | |
404 (set = $setter:ident, item = $item_type:path) => { | |
405 fn $setter(&mut self, value: Option<&str>) -> Result<()> { | |
406 unsafe { self.set_cstr_item($item_type, value) } | |
407 } | |
408 }; | |
409 } | |
410 | |
192 impl PamHandle for LibPamHandle { | 411 impl PamHandle for LibPamHandle { |
193 fn get_user(&mut self, prompt: Option<&str>) -> crate::Result<String> { | 412 type Conv = LibPamConversation; |
413 fn get_user(&mut self, prompt: Option<&str>) -> Result<Option<&str>> { | |
194 let prompt = memory::option_cstr(prompt)?; | 414 let prompt = memory::option_cstr(prompt)?; |
195 let mut output: *const c_char = ptr::null_mut(); | 415 let mut output: *const c_char = ptr::null(); |
196 let ret = unsafe { | 416 let ret = unsafe { |
197 pam_ffi::pam_get_user(&self.0, &mut output, memory::prompt_ptr(prompt.as_ref())) | 417 pam_ffi::pam_get_user(self, &mut output, memory::prompt_ptr(prompt.as_ref())) |
198 }; | 418 }; |
199 ErrorCode::result_from(ret)?; | 419 ErrorCode::result_from(ret)?; |
200 unsafe {memory::copy_pam_string(output)} | 420 unsafe { memory::wrap_string(output) } |
201 } | 421 } |
202 | 422 |
203 fn get_authtok(&mut self, prompt: Option<&str>) -> crate::Result<SecureString> { | 423 cstr_item!(get = user_item, item = ItemType::User); |
424 cstr_item!(set = set_user_item, item = ItemType::User); | |
425 cstr_item!(get = service, item = ItemType::Service); | |
426 cstr_item!(set = set_service, item = ItemType::Service); | |
427 cstr_item!(get = user_prompt, item = ItemType::UserPrompt); | |
428 cstr_item!(set = set_user_prompt, item = ItemType::UserPrompt); | |
429 cstr_item!(get = tty_name, item = ItemType::Tty); | |
430 cstr_item!(set = set_tty_name, item = ItemType::Tty); | |
431 cstr_item!(get = remote_user, item = ItemType::RemoteUser); | |
432 cstr_item!(set = set_remote_user, item = ItemType::RemoteUser); | |
433 cstr_item!(get = remote_host, item = ItemType::RemoteHost); | |
434 cstr_item!(set = set_remote_host, item = ItemType::RemoteHost); | |
435 cstr_item!(set = set_authtok_item, item = ItemType::AuthTok); | |
436 cstr_item!(set = set_old_authtok_item, item = ItemType::OldAuthTok); | |
437 } | |
438 | |
439 impl PamApplicationHandle for LibPamHandle { | |
440 fn close(mut self, status: Result<()>) -> Result<()> { | |
441 let result = unsafe { pam_ffi::pam_end(&mut self, ErrorCode::result_to_c(status)) }; | |
442 // Since we've already `pam_end`ed this session, we don't want it to be | |
443 // double-freed on drop. | |
444 mem::forget(self); | |
445 ErrorCode::result_from(result) | |
446 } | |
447 | |
448 fn set_conversation(&mut self, conversation: Self::Conv) -> Result<()> { | |
449 todo!() | |
450 } | |
451 } | |
452 | |
453 impl PamModuleHandle for LibPamHandle { | |
454 fn conversation(&mut self) -> Result<ConversationMux<'_, Self::Conv>> { | |
455 todo!() | |
456 } | |
457 | |
458 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<Option<&str>> { | |
204 let prompt = memory::option_cstr(prompt)?; | 459 let prompt = memory::option_cstr(prompt)?; |
205 let mut output: *const c_char = ptr::null_mut(); | 460 let mut output: *const c_char = ptr::null_mut(); |
206 let res = unsafe { | 461 let res = unsafe { |
207 pam_ffi::pam_get_authtok( | 462 pam_ffi::pam_get_authtok( |
208 &self.0, | 463 self, |
209 ItemType::AuthTok.into(), | 464 ItemType::AuthTok.into(), |
210 &mut output, | 465 &mut output, |
211 memory::prompt_ptr(prompt.as_ref()), | 466 memory::prompt_ptr(prompt.as_ref()), |
212 ) | 467 ) |
213 }; | 468 }; |
214 ErrorCode::result_from(res)?; | 469 ErrorCode::result_from(res)?; |
215 unsafe {memory::copy_pam_string(output)}.map(SecureString::from) | 470 unsafe { memory::wrap_string(output) } |
216 } | 471 } |
217 | 472 |
218 fn get_item<T: Item>(&mut self) -> Result<Option<T>> { | 473 cstr_item!(get = authtok_item, item = ItemType::AuthTok); |
219 let mut ptr: *const c_void = ptr::null(); | 474 cstr_item!(get = old_authtok_item, item = ItemType::OldAuthTok); |
220 let out = unsafe { | |
221 let ret = pam_ffi::pam_get_item(&self.0, T::type_id().into(), &mut ptr); | |
222 ErrorCode::result_from(ret)?; | |
223 (ptr as *const T::Raw).as_ref().map(|p| T::from_raw(p)) | |
224 }; | |
225 Ok(out) | |
226 } | |
227 | |
228 fn set_item<T: Item>(&mut self, item: T) -> Result<()> { | |
229 let ret = unsafe { | |
230 pam_ffi::pam_set_item(&mut self.0, T::type_id().into(), item.into_raw().cast()) | |
231 }; | |
232 ErrorCode::result_from(ret) | |
233 } | |
234 } | |
235 | |
236 impl PamApplicationHandle for LibPamHandle { | |
237 fn close(mut self, status: Result<()>) -> Result<()> { | |
238 let result = unsafe { pam_ffi::pam_end(&mut self.0, ErrorCode::result_to_c(status)) }; | |
239 // Since we've already `pam_end`ed this session, we don't want it to be | |
240 // double-freed on drop. | |
241 mem::forget(self); | |
242 ErrorCode::result_from(result) | |
243 } | |
244 } | |
245 | |
246 impl PamModuleHandle for LibPamHandle { | |
247 unsafe fn get_data<T>(&mut self, key: &str) -> crate::Result<Option<&T>> { | |
248 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; | |
249 let mut ptr: *const c_void = ptr::null(); | |
250 ErrorCode::result_from(pam_ffi::pam_get_data(&self.0, c_key.as_ptr(), &mut ptr))?; | |
251 Ok((ptr as *const T).as_ref()) | |
252 } | |
253 | |
254 fn set_data<T>(&mut self, key: &str, data: Box<T>) -> crate::Result<()> { | |
255 let c_key = CString::new(key).map_err(|_| ErrorCode::ConversationError)?; | |
256 let ret = unsafe { | |
257 pam_ffi::pam_set_data( | |
258 &mut self.0, | |
259 c_key.as_ptr(), | |
260 Box::into_raw(data).cast(), | |
261 set_data_cleanup::<T>, | |
262 ) | |
263 }; | |
264 ErrorCode::result_from(ret) | |
265 } | |
266 } | 475 } |
267 | 476 |
268 /// Function called at the end of a PAM session that is called to clean up | 477 /// Function called at the end of a PAM session that is called to clean up |
269 /// a value previously provided to PAM in a `pam_set_data` call. | 478 /// a value previously provided to PAM in a `pam_set_data` call. |
270 /// | 479 /// |