comparison src/handle.rs @ 80:5aa1a010f1e8

Start using PAM headers; improve owned/borrowed distinction. - Uses bindgen to generate bindings (only if needed). - Gets the story together on owned vs. borrowed handles. - Reduces number of mutable borrows in handle operation (since `PamHandle` is neither `Send` nor `Sync`, we never have to worry about thread safety. - Improves a bunch of macros so we don't have our own special syntax for docs. - Implement question indirection for standard XSSO PAM implementations.
author Paul Fisher <paul@pfish.zone>
date Tue, 10 Jun 2025 01:09:30 -0400
parents 002adfb98c5c
children
comparison
equal deleted inserted replaced
79:2128123b9406 80:5aa1a010f1e8
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
2 use crate::constants::Result; 3 use crate::constants::Result;
3 use crate::conv::Conversation; 4 use crate::conv::Conversation;
4 5
5 macro_rules! trait_item { 6 macro_rules! trait_item {
6 (get = $getter:ident, item = $item:literal $(, see = $see:path)? $(, $($doc:literal)*)?) => { 7 ($(#[$md:meta])* get = $getter:ident, item = $item:literal $(, see = $see:path)?) => {
7 $( 8 $(#[$md])*
8 $(#[doc = $doc])* 9 #[doc = ""]
9 #[doc = ""]
10 )?
11 #[doc = concat!("Gets the `", $item, "` of the PAM handle.")] 10 #[doc = concat!("Gets the `", $item, "` of the PAM handle.")]
12 $( 11 $(
13 #[doc = concat!("See [`", stringify!($see), "`].")] 12 #[doc = concat!("See [`", stringify!($see), "`].")]
14 )? 13 )?
15 #[doc = ""] 14 ///
16 #[doc = "Returns a reference to the item's value, owned by PAM."] 15 /// Returns a reference to the item's value, owned by PAM.
17 #[doc = "The item is assumed to be valid UTF-8 text."] 16 /// The item is assumed to be valid UTF-8 text.
18 #[doc = "If it is not, `ConversationError` is returned."] 17 /// If it is not, `ConversationError` is returned.
19 #[doc = ""] 18 ///
20 #[doc = "See the [`pam_get_item`][man] manual page,"] 19 /// See the [`pam_get_item`][man] manual page,
21 #[doc = "[`pam_get_item` in the Module Writers' Guide][mwg], or"] 20 /// [`pam_get_item` in the Module Writers' Guide][mwg], or
22 #[doc = "[`pam_get_item` in the Application Developers' Guide][adg]."] 21 /// [`pam_get_item` in the Application Developers' Guide][adg].
23 #[doc = ""] 22 ///
24 #[doc = "[man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html"] 23 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html
25 #[doc = "[adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_get_item"] 24 /// [adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_get_item
26 #[doc = "[mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item"] 25 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item
27 fn $getter(&mut self) -> Result<Option<&str>>; 26 fn $getter(&self) -> Result<Option<&str>>;
28 }; 27 };
29 (set = $setter:ident, item = $item:literal $(, see = $see:path)? $(, $($doc:literal)*)?) => { 28 ($(#[$md:meta])* set = $setter:ident, item = $item:literal $(, see = $see:path)?) => {
30 $( 29 $(#[$md])*
31 $(#[doc = $doc])*
32 #[doc = ""]
33 )?
34 #[doc = concat!("Sets the `", $item, "` from the PAM handle.")] 30 #[doc = concat!("Sets the `", $item, "` from the PAM handle.")]
35 $( 31 $(
36 #[doc = concat!("See [`", stringify!($see), "`].")] 32 #[doc = concat!("See [`", stringify!($see), "`].")]
37 )? 33 )?
38 #[doc = ""] 34 ///
39 #[doc = "Sets the item's value. PAM copies the string's contents."] 35 /// Sets the item's value. PAM copies the string's contents.
40 #[doc = "If the string contains a null byte, this will return "] 36 /// If the string contains a null byte, this will return
41 #[doc = "a `ConversationError`."] 37 /// a `ConversationError`.
42 #[doc = ""] 38 ///
43 #[doc = "See the [`pam_set_item`][man] manual page,"] 39 /// See the [`pam_set_item`][man] manual page,
44 #[doc = "[`pam_set_item` in the Module Writers' Guide][mwg], or"] 40 /// [`pam_set_item` in the Module Writers' Guide][mwg], or
45 #[doc = "[`pam_set_item` in the Application Developers' Guide][adg]."] 41 /// [`pam_set_item` in the Application Developers' Guide][adg].
46 #[doc = ""] 42 ///
47 #[doc = "[man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html"] 43 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html
48 #[doc = "[adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_set_item"] 44 /// [adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_set_item
49 #[doc = "[mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item"] 45 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item
50 fn $setter(&mut self, value: Option<&str>) -> Result<()>; 46 fn $setter(&mut self, value: Option<&str>) -> Result<()>;
51 }; 47 };
52 } 48 }
53
54 /// All-in-one trait for what you should expect from PAM as an application.
55 pub trait PamHandleApplication: PamApplicationOnly + PamShared {}
56 impl<T> PamHandleApplication for T where T: PamApplicationOnly + PamShared {}
57
58 /// All-in-one trait for what you should expect from PAM as a module.
59 pub trait PamHandleModule: PamModuleOnly + PamShared {}
60 impl<T> PamHandleModule for T where T: PamModuleOnly + PamShared {}
61 49
62 /// Functionality for both PAM applications and PAM modules. 50 /// Functionality for both PAM applications and PAM modules.
63 /// 51 ///
64 /// This base trait includes features of a PAM handle that are available 52 /// This base trait includes features of a PAM handle that are available
65 /// to both applications and modules. 53 /// to both applications and modules.
98 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html 86 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html
99 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user 87 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user
100 fn get_user(&mut self, prompt: Option<&str>) -> Result<&str>; 88 fn get_user(&mut self, prompt: Option<&str>) -> Result<&str>;
101 89
102 trait_item!( 90 trait_item!(
91 /// The identity of the user for whom service is being requested.
92 ///
93 /// Unlike [`get_user`](Self::get_user), this will simply get
94 /// the current state of the user item, and not request the username.
95 /// While PAM usually sets this automatically in the `get_user` call,
96 /// it may be changed by a module during the PAM transaction.
97 /// Applications should check it after each step of the PAM process.
103 get = user_item, 98 get = user_item,
104 item = "PAM_USER", 99 item = "PAM_USER",
105 see = Self::get_user, 100 see = Self::get_user
106 "The identity of the user for whom service is being requested." 101 );
107 "" 102 trait_item!(
108 "Unlike [`get_user`](Self::get_user), this will simply get" 103 /// Sets the identity of the logging-in user.
109 "the current state of the user item, and not request the username. " 104 ///
110 "While PAM usually sets this automatically in the `get_user` call, " 105 /// Usually this will be set during the course of
111 "it may be changed by a module during the PAM transaction. " 106 /// a [`get_user`](Self::get_user) call, but you may set it manually
112 "Applications should check it after each step of the PAM process." 107 /// or change it during the PAM process.
113 );
114 trait_item!(
115 set = set_user_item, 108 set = set_user_item,
116 item = "PAM_USER", 109 item = "PAM_USER",
117 see = Self::user_item, 110 see = Self::user_item
118 "Sets the identity of the logging-in user." 111 );
119 "" 112
120 "Usually this will be set during the course of " 113 trait_item!(
121 "a [`get_user`](Self::get_user) call, but you may set it manually " 114 /// The service name, which identifies the PAM stack which is used
122 "or change it during the PAM process." 115 /// to perform authentication.
123 );
124
125 trait_item!(
126 get = service, 116 get = service,
127 item = "PAM_SERVICE", 117 item = "PAM_SERVICE"
128 "The service name, which identifies the PAM stack which is used " 118 );
129 "to perform authentication." 119 trait_item!(
130 ); 120 /// The service name, which identifies the PAM stack which is used
131 trait_item!( 121 /// to perform authentication. It's probably a bad idea to change this.
132 set = set_service, 122 set = set_service,
133 item = "PAM_SERVICE", 123 item = "PAM_SERVICE",
134 see = Self::service, 124 see = Self::service
135 "The service name, which identifies the PAM stack which is used " 125 );
136 "to perform authentication. It's probably a bad idea to change this." 126
137 ); 127 trait_item!(
138 128 /// The string used to prompt for a user's name.
139 trait_item!( 129 /// By default, this is a localized version of `login: `.
140 get = user_prompt, 130 get = user_prompt,
141 item = "PAM_USER_PROMPT", 131 item = "PAM_USER_PROMPT"
142 "The string used to prompt for a user's name." 132 );
143 "By default, this is a localized version of `login: `." 133 trait_item!(
144 ); 134 /// Sets the string used to prompt for a user's name.
145 trait_item!(
146 set = set_user_prompt, 135 set = set_user_prompt,
147 item = "PAM_USER_PROMPT", 136 item = "PAM_USER_PROMPT",
148 see = Self::user_prompt, 137 see = Self::user_prompt
149 "Sets the string used to prompt for a user's name." 138 );
150 ); 139
151 140 trait_item!(
152 trait_item!( 141 /// "The terminal name prefixed by /dev/ for device files."
142 ///
143 /// This is the terminal the user is logging in on.
144 /// Very old applications may use this instead of `PAM_XDISPLAY`.
153 get = tty_name, 145 get = tty_name,
154 item = "PAM_TTY", 146 item = "PAM_TTY"
155 "\"The terminal name prefixed by /dev/ for device files.\"" 147 );
156 "" 148 trait_item!(
157 "This is the terminal the user is logging in on." 149 /// Sets the terminal name.
158 "Very old applications may use this instead of `PAM_XDISPLAY`." 150 ///
159 ); 151 /// (TODO: See if libpam sets this itself or if the application does.)
160 trait_item!(
161 set = set_tty_name, 152 set = set_tty_name,
162 item = "PAM_TTY", 153 item = "PAM_TTY",
163 see = Self::tty_name, 154 see = Self::tty_name
164 "Sets the terminal name." 155 );
165 "" 156
166 "(TODO: See if libpam sets this itself or if the application does.)" 157 trait_item!(
167 ); 158 /// If set, the identity of the remote user logging in.
168 159 ///
169 trait_item!( 160 /// This is only as trustworthy as the application calling PAM.
161 /// Also see [`remote_host`](Self::remote_host).
170 get = remote_user, 162 get = remote_user,
171 item = "PAM_RUSER", 163 item = "PAM_RUSER"
172 "If set, the identity of the remote user logging in." 164 );
173 "" 165 trait_item!(
174 "This is only as trustworthy as the application calling PAM." 166 /// Sets the identity of the remote user logging in.
175 "Also see [`remote_host`](Self::remote_host)." 167 ///
176 ); 168 /// This is usually set by the application before making calls
177 trait_item!( 169 /// into a PAM session. (TODO: check this!)
178 set = set_remote_user, 170 set = set_remote_user,
179 item = "PAM_RUSER", 171 item = "PAM_RUSER"
180 "Sets the identity of the remote user logging in." 172 );
181 "" 173
182 "This is usually set by the application before making calls " 174 trait_item!(
183 "into a PAM session. (TODO: check this!)" 175 /// If set, the remote location where the user is coming from.
184 ); 176 ///
185 177 /// This is only as trustworthy as the application calling PAM.
186 trait_item!( 178 /// This can be combined with [`Self::remote_user`] to identify
179 /// the account the user is attempting to log in from,
180 /// with `remote_user@remote_host`.
181 ///
182 /// If unset, "it is unclear where the authentication request
183 /// is originating from."
187 get = remote_host, 184 get = remote_host,
188 item = "PAM_RHOST", 185 item = "PAM_RHOST"
189 "If set, the remote location where the user is coming from." 186 );
190 "" 187 trait_item!(
191 "This is only as trustworthy as the application calling PAM. " 188 /// Sets the location where the user is coming from.
192 "This can be combined with [`Self::remote_user`] to identify " 189 ///
193 "the account the user is attempting to log in from, " 190 /// This is usually set by the application before making calls
194 "with `remote_user@remote_host`." 191 /// into a PAM session. (TODO: check this!)
195 ""
196 "If unset, \"it is unclear where the authentication request "
197 "is originating from.\""
198 );
199 trait_item!(
200 set = set_remote_host, 192 set = set_remote_host,
201 item = "PAM_RHOST", 193 item = "PAM_RHOST",
202 see = Self::remote_host, 194 see = Self::remote_host
203 "Sets the location where the user is coming from." 195 );
204 "" 196
205 "This is usually set by the application before making calls " 197 trait_item!(
206 "into a PAM session. (TODO: check this!)" 198 /// Gets the user's authentication token (e.g., password).
207 ); 199 ///
208 200 /// This is usually set automatically when
209 trait_item!( 201 /// [`get_authtok`](PamHandleModule::get_authtok) is called,
202 /// but can be manually set.
210 set = set_authtok_item, 203 set = set_authtok_item,
211 item = "PAM_AUTHTOK", 204 item = "PAM_AUTHTOK",
212 see = PamModuleOnly::authtok_item, 205 see = PamHandleModule::authtok_item
213 "Gets the user's authentication token (e.g., password)." 206 );
214 "" 207
215 "This is usually set automatically when " 208 trait_item!(
216 "[`get_authtok`](PamModuleOnly::get_authtok) is called, " 209 /// Sets the user's "old authentication token" when changing passwords.
217 "but can be manually set." 210 //
218 ); 211 /// This is usually set automatically by PAM.
219
220 trait_item!(
221 set = set_old_authtok_item, 212 set = set_old_authtok_item,
222 item = "PAM_OLDAUTHTOK", 213 item = "PAM_OLDAUTHTOK",
223 see = PamModuleOnly::old_authtok_item, 214 see = PamHandleModule::old_authtok_item
224 "Sets the user's \"old authentication token\" when changing passwords."
225 ""
226 "This is usually set automatically by PAM."
227 ); 215 );
228 } 216 }
229 217
230 /// Functionality of a PAM handle that can be expected by a PAM application. 218 /// Functionality of a PAM handle that can be expected by a PAM application.
231 /// 219 ///
232 /// If you are not writing a PAM client application (e.g., you are writing 220 /// If you are not writing a PAM client application (e.g., you are writing
233 /// a module), you should not use the functionality exposed by this trait. 221 /// a module), you should not use the functionality exposed by this trait.
234 /// 222 ///
235 /// Like [`PamShared`], this is intended to allow creating mock implementations 223 /// Like [`PamShared`], this is intended to allow creating mock implementations
236 /// of PAM for testing PAM applications. 224 /// of PAM for testing PAM applications.
237 pub trait PamApplicationOnly { 225 pub trait PamHandleApplication: PamShared {
238 /// Closes the PAM session on an owned PAM handle. 226 // reserved!
239 ///
240 /// This should be called with the result of the application's last call
241 /// into PAM services. Since this is only applicable to *owned* PAM handles,
242 /// a PAM module should never call this (and it will never be handed
243 /// an owned `PamHandle` that it can `close`).
244 ///
245 /// See the [`pam_end` manual page][man] for more information.
246 ///
247 /// ```no_run
248 /// # use nonstick::handle::PamApplicationOnly;
249 /// # use std::error::Error;
250 /// # fn _doc(handle: impl PamApplicationOnly, auth_result: nonstick::Result<()>) -> Result<(), Box<dyn Error>> {
251 /// // Earlier: authentication was performed and the result was stored
252 /// // into auth_result.
253 /// handle.close(auth_result)?;
254 /// # Ok(())
255 /// # }
256 /// ```
257 ///
258 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_end.3.html
259 fn close(self, status: Result<()>) -> Result<()>;
260 } 227 }
261 228
262 /// Functionality of a PAM handle that can be expected by a PAM module. 229 /// Functionality of a PAM handle that can be expected by a PAM module.
263 /// 230 ///
264 /// If you are not writing a PAM module (e.g., you are writing an application), 231 /// If you are not writing a PAM module (e.g., you are writing an application),
265 /// you should not use any of the functionality exposed by this trait. 232 /// you should not use any of the functionality exposed by this trait.
266 /// 233 ///
267 /// Like [`PamShared`], this is intended to allow creating mock implementations 234 /// Like [`PamShared`], this is intended to allow creating mock implementations
268 /// of PAM for testing PAM modules. 235 /// of PAM for testing PAM modules.
269 pub trait PamModuleOnly: Conversation { 236 pub trait PamHandleModule: Conversation + PamShared {
270 /// Retrieves the authentication token from the user. 237 /// Retrieves the authentication token from the user.
271 /// 238 ///
272 /// This should only be used by *authentication* and *password-change* 239 /// This should only be used by *authentication* and *password-change*
273 /// PAM modules. 240 /// PAM modules.
274 /// 241 ///
276 /// or [`pam_get_item` in the Module Writer's Guide][mwg]. 243 /// or [`pam_get_item` in the Module Writer's Guide][mwg].
277 /// 244 ///
278 /// # Example 245 /// # Example
279 /// 246 ///
280 /// ```no_run 247 /// ```no_run
281 /// # use nonstick::handle::PamModuleOnly; 248 /// # use nonstick::handle::PamHandleModule;
282 /// # fn _doc(handle: &mut impl PamModuleOnly) -> Result<(), Box<dyn std::error::Error>> { 249 /// # fn _doc(handle: &mut impl PamHandleModule) -> Result<(), Box<dyn std::error::Error>> {
283 /// // Get the user's password using the default prompt. 250 /// // Get the user's password using the default prompt.
284 /// let pass = handle.get_authtok(None)?; 251 /// let pass = handle.get_authtok(None)?;
285 /// // Get the user's password using a custom prompt. 252 /// // Get the user's password using a custom prompt.
286 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; 253 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?;
287 /// Ok(()) 254 /// Ok(())
291 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html 258 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html
292 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item 259 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item
293 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<&str>; 260 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<&str>;
294 261
295 trait_item!( 262 trait_item!(
263 /// Gets the user's authentication token (e.g., password).
264 ///
265 /// This is normally set automatically by PAM when calling
266 /// [`get_authtok`](Self::get_authtok), but can be set explicitly.
267 ///
268 /// Like `get_authtok`, this should only ever be called
269 /// by *authentication* and *password-change* PAM modules.
296 get = authtok_item, 270 get = authtok_item,
297 item = "PAM_AUTHTOK", 271 item = "PAM_AUTHTOK",
298 see = Self::get_authtok, 272 see = Self::get_authtok
299 "Gets the user's authentication token (e.g., password)." 273 );
300 "" 274
301 "This is normally set automatically by PAM when calling " 275 trait_item!(
302 "[`get_authtok`](Self::get_authtok), but can be set explicitly." 276 /// Gets the user's old authentication token when changing passwords.
303 "" 277 ///
304 "Like `get_authtok`, this should only ever be called " 278 /// This should only ever be called by *password-change* PAM modules.
305 "by *authentication* and *password-change* PAM modules."
306 );
307
308 trait_item!(
309 get = old_authtok_item, 279 get = old_authtok_item,
310 item = "PAM_OLDAUTHTOK", 280 item = "PAM_OLDAUTHTOK",
311 see = PamShared::set_old_authtok_item, 281 see = PamShared::set_old_authtok_item
312 "Gets the user's old authentication token when changing passwords."
313 ""
314 "This should only ever be called by *password-change* PAM modules."
315 ); 282 );
316 283
317 /* 284 /*
318 TODO: Re-enable this at some point. 285 TODO: Re-enable this at some point.
319 /// Gets some pointer, identified by `key`, that has been set previously 286 /// Gets some pointer, identified by `key`, that has been set previously