Mercurial > crates > nonstick
diff 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 |
line wrap: on
line diff
--- a/src/handle.rs Sun Jun 08 04:21:58 2025 -0400 +++ b/src/handle.rs Tue Jun 10 01:09:30 2025 -0400 @@ -1,64 +1,52 @@ //! The wrapper types and traits for handles into the PAM library. + use crate::constants::Result; use crate::conv::Conversation; macro_rules! trait_item { - (get = $getter:ident, item = $item:literal $(, see = $see:path)? $(, $($doc:literal)*)?) => { - $( - $(#[doc = $doc])* - #[doc = ""] - )? + ($(#[$md:meta])* get = $getter:ident, item = $item:literal $(, see = $see:path)?) => { + $(#[$md])* + #[doc = ""] #[doc = concat!("Gets the `", $item, "` of the PAM handle.")] $( #[doc = concat!("See [`", stringify!($see), "`].")] )? - #[doc = ""] - #[doc = "Returns a reference to the item's value, owned by PAM."] - #[doc = "The item is assumed to be valid UTF-8 text."] - #[doc = "If it is not, `ConversationError` is returned."] - #[doc = ""] - #[doc = "See the [`pam_get_item`][man] manual page,"] - #[doc = "[`pam_get_item` in the Module Writers' Guide][mwg], or"] - #[doc = "[`pam_get_item` in the Application Developers' Guide][adg]."] - #[doc = ""] - #[doc = "[man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html"] - #[doc = "[adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_get_item"] - #[doc = "[mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item"] - fn $getter(&mut self) -> Result<Option<&str>>; + /// + /// Returns a reference to the item's value, owned by PAM. + /// The item is assumed to be valid UTF-8 text. + /// If it is not, `ConversationError` is returned. + /// + /// See the [`pam_get_item`][man] manual page, + /// [`pam_get_item` in the Module Writers' Guide][mwg], or + /// [`pam_get_item` in the Application Developers' Guide][adg]. + /// + /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html + /// [adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_get_item + /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item + fn $getter(&self) -> Result<Option<&str>>; }; - (set = $setter:ident, item = $item:literal $(, see = $see:path)? $(, $($doc:literal)*)?) => { - $( - $(#[doc = $doc])* - #[doc = ""] - )? + ($(#[$md:meta])* set = $setter:ident, item = $item:literal $(, see = $see:path)?) => { + $(#[$md])* #[doc = concat!("Sets the `", $item, "` from the PAM handle.")] $( #[doc = concat!("See [`", stringify!($see), "`].")] )? - #[doc = ""] - #[doc = "Sets the item's value. PAM copies the string's contents."] - #[doc = "If the string contains a null byte, this will return "] - #[doc = "a `ConversationError`."] - #[doc = ""] - #[doc = "See the [`pam_set_item`][man] manual page,"] - #[doc = "[`pam_set_item` in the Module Writers' Guide][mwg], or"] - #[doc = "[`pam_set_item` in the Application Developers' Guide][adg]."] - #[doc = ""] - #[doc = "[man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html"] - #[doc = "[adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_set_item"] - #[doc = "[mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item"] + /// + /// Sets the item's value. PAM copies the string's contents. + /// If the string contains a null byte, this will return + /// a `ConversationError`. + /// + /// See the [`pam_set_item`][man] manual page, + /// [`pam_set_item` in the Module Writers' Guide][mwg], or + /// [`pam_set_item` in the Application Developers' Guide][adg]. + /// + /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html + /// [adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_set_item + /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item fn $setter(&mut self, value: Option<&str>) -> Result<()>; }; } -/// All-in-one trait for what you should expect from PAM as an application. -pub trait PamHandleApplication: PamApplicationOnly + PamShared {} -impl<T> PamHandleApplication for T where T: PamApplicationOnly + PamShared {} - -/// All-in-one trait for what you should expect from PAM as a module. -pub trait PamHandleModule: PamModuleOnly + PamShared {} -impl<T> PamHandleModule for T where T: PamModuleOnly + PamShared {} - /// Functionality for both PAM applications and PAM modules. /// /// This base trait includes features of a PAM handle that are available @@ -100,130 +88,130 @@ fn get_user(&mut self, prompt: Option<&str>) -> Result<&str>; trait_item!( + /// The identity of the user for whom service is being requested. + /// + /// Unlike [`get_user`](Self::get_user), this will simply get + /// the current state of the user item, and not request the username. + /// While PAM usually sets this automatically in the `get_user` call, + /// it may be changed by a module during the PAM transaction. + /// Applications should check it after each step of the PAM process. get = user_item, item = "PAM_USER", - see = Self::get_user, - "The identity of the user for whom service is being requested." - "" - "Unlike [`get_user`](Self::get_user), this will simply get" - "the current state of the user item, and not request the username. " - "While PAM usually sets this automatically in the `get_user` call, " - "it may be changed by a module during the PAM transaction. " - "Applications should check it after each step of the PAM process." + see = Self::get_user ); trait_item!( + /// Sets the identity of the logging-in user. + /// + /// Usually this will be set during the course of + /// a [`get_user`](Self::get_user) call, but you may set it manually + /// or change it during the PAM process. set = set_user_item, item = "PAM_USER", - see = Self::user_item, - "Sets the identity of the logging-in user." - "" - "Usually this will be set during the course of " - "a [`get_user`](Self::get_user) call, but you may set it manually " - "or change it during the PAM process." + see = Self::user_item ); trait_item!( + /// The service name, which identifies the PAM stack which is used + /// to perform authentication. get = service, - item = "PAM_SERVICE", - "The service name, which identifies the PAM stack which is used " - "to perform authentication." + item = "PAM_SERVICE" ); trait_item!( + /// The service name, which identifies the PAM stack which is used + /// to perform authentication. It's probably a bad idea to change this. set = set_service, item = "PAM_SERVICE", - see = Self::service, - "The service name, which identifies the PAM stack which is used " - "to perform authentication. It's probably a bad idea to change this." + see = Self::service ); trait_item!( + /// The string used to prompt for a user's name. + /// By default, this is a localized version of `login: `. get = user_prompt, - item = "PAM_USER_PROMPT", - "The string used to prompt for a user's name." - "By default, this is a localized version of `login: `." + item = "PAM_USER_PROMPT" ); trait_item!( + /// Sets the string used to prompt for a user's name. set = set_user_prompt, item = "PAM_USER_PROMPT", - see = Self::user_prompt, - "Sets the string used to prompt for a user's name." + see = Self::user_prompt ); trait_item!( + /// "The terminal name prefixed by /dev/ for device files." + /// + /// This is the terminal the user is logging in on. + /// Very old applications may use this instead of `PAM_XDISPLAY`. get = tty_name, - item = "PAM_TTY", - "\"The terminal name prefixed by /dev/ for device files.\"" - "" - "This is the terminal the user is logging in on." - "Very old applications may use this instead of `PAM_XDISPLAY`." + item = "PAM_TTY" ); trait_item!( + /// Sets the terminal name. + /// + /// (TODO: See if libpam sets this itself or if the application does.) set = set_tty_name, item = "PAM_TTY", - see = Self::tty_name, - "Sets the terminal name." - "" - "(TODO: See if libpam sets this itself or if the application does.)" + see = Self::tty_name ); trait_item!( + /// If set, the identity of the remote user logging in. + /// + /// This is only as trustworthy as the application calling PAM. + /// Also see [`remote_host`](Self::remote_host). get = remote_user, - item = "PAM_RUSER", - "If set, the identity of the remote user logging in." - "" - "This is only as trustworthy as the application calling PAM." - "Also see [`remote_host`](Self::remote_host)." + item = "PAM_RUSER" ); trait_item!( + /// Sets the identity of the remote user logging in. + /// + /// This is usually set by the application before making calls + /// into a PAM session. (TODO: check this!) set = set_remote_user, - item = "PAM_RUSER", - "Sets the identity of the remote user logging in." - "" - "This is usually set by the application before making calls " - "into a PAM session. (TODO: check this!)" + item = "PAM_RUSER" ); trait_item!( + /// If set, the remote location where the user is coming from. + /// + /// This is only as trustworthy as the application calling PAM. + /// This can be combined with [`Self::remote_user`] to identify + /// the account the user is attempting to log in from, + /// with `remote_user@remote_host`. + /// + /// If unset, "it is unclear where the authentication request + /// is originating from." get = remote_host, - item = "PAM_RHOST", - "If set, the remote location where the user is coming from." - "" - "This is only as trustworthy as the application calling PAM. " - "This can be combined with [`Self::remote_user`] to identify " - "the account the user is attempting to log in from, " - "with `remote_user@remote_host`." - "" - "If unset, \"it is unclear where the authentication request " - "is originating from.\"" + item = "PAM_RHOST" ); trait_item!( + /// Sets the location where the user is coming from. + /// + /// This is usually set by the application before making calls + /// into a PAM session. (TODO: check this!) set = set_remote_host, item = "PAM_RHOST", - see = Self::remote_host, - "Sets the location where the user is coming from." - "" - "This is usually set by the application before making calls " - "into a PAM session. (TODO: check this!)" + see = Self::remote_host ); trait_item!( + /// Gets the user's authentication token (e.g., password). + /// + /// This is usually set automatically when + /// [`get_authtok`](PamHandleModule::get_authtok) is called, + /// but can be manually set. set = set_authtok_item, item = "PAM_AUTHTOK", - see = PamModuleOnly::authtok_item, - "Gets the user's authentication token (e.g., password)." - "" - "This is usually set automatically when " - "[`get_authtok`](PamModuleOnly::get_authtok) is called, " - "but can be manually set." + see = PamHandleModule::authtok_item ); trait_item!( + /// Sets the user's "old authentication token" when changing passwords. + // + /// This is usually set automatically by PAM. set = set_old_authtok_item, item = "PAM_OLDAUTHTOK", - see = PamModuleOnly::old_authtok_item, - "Sets the user's \"old authentication token\" when changing passwords." - "" - "This is usually set automatically by PAM." + see = PamHandleModule::old_authtok_item ); } @@ -234,29 +222,8 @@ /// /// Like [`PamShared`], this is intended to allow creating mock implementations /// of PAM for testing PAM applications. -pub trait PamApplicationOnly { - /// Closes the PAM session on an owned PAM handle. - /// - /// This should be called with the result of the application's last call - /// into PAM services. Since this is only applicable to *owned* PAM handles, - /// a PAM module should never call this (and it will never be handed - /// an owned `PamHandle` that it can `close`). - /// - /// See the [`pam_end` manual page][man] for more information. - /// - /// ```no_run - /// # use nonstick::handle::PamApplicationOnly; - /// # use std::error::Error; - /// # fn _doc(handle: impl PamApplicationOnly, auth_result: nonstick::Result<()>) -> Result<(), Box<dyn Error>> { - /// // Earlier: authentication was performed and the result was stored - /// // into auth_result. - /// handle.close(auth_result)?; - /// # Ok(()) - /// # } - /// ``` - /// - /// [man]: https://www.man7.org/linux/man-pages/man3/pam_end.3.html - fn close(self, status: Result<()>) -> Result<()>; +pub trait PamHandleApplication: PamShared { + // reserved! } /// Functionality of a PAM handle that can be expected by a PAM module. @@ -266,7 +233,7 @@ /// /// Like [`PamShared`], this is intended to allow creating mock implementations /// of PAM for testing PAM modules. -pub trait PamModuleOnly: Conversation { +pub trait PamHandleModule: Conversation + PamShared { /// Retrieves the authentication token from the user. /// /// This should only be used by *authentication* and *password-change* @@ -278,8 +245,8 @@ /// # Example /// /// ```no_run - /// # use nonstick::handle::PamModuleOnly; - /// # fn _doc(handle: &mut impl PamModuleOnly) -> Result<(), Box<dyn std::error::Error>> { + /// # use nonstick::handle::PamHandleModule; + /// # fn _doc(handle: &mut impl PamHandleModule) -> Result<(), Box<dyn std::error::Error>> { /// // Get the user's password using the default prompt. /// let pass = handle.get_authtok(None)?; /// // Get the user's password using a custom prompt. @@ -293,25 +260,25 @@ fn get_authtok(&mut self, prompt: Option<&str>) -> Result<&str>; trait_item!( + /// Gets the user's authentication token (e.g., password). + /// + /// This is normally set automatically by PAM when calling + /// [`get_authtok`](Self::get_authtok), but can be set explicitly. + /// + /// Like `get_authtok`, this should only ever be called + /// by *authentication* and *password-change* PAM modules. get = authtok_item, item = "PAM_AUTHTOK", - see = Self::get_authtok, - "Gets the user's authentication token (e.g., password)." - "" - "This is normally set automatically by PAM when calling " - "[`get_authtok`](Self::get_authtok), but can be set explicitly." - "" - "Like `get_authtok`, this should only ever be called " - "by *authentication* and *password-change* PAM modules." + see = Self::get_authtok ); trait_item!( + /// Gets the user's old authentication token when changing passwords. + /// + /// This should only ever be called by *password-change* PAM modules. get = old_authtok_item, item = "PAM_OLDAUTHTOK", - see = PamShared::set_old_authtok_item, - "Gets the user's old authentication token when changing passwords." - "" - "This should only ever be called by *password-change* PAM modules." + see = PamShared::set_old_authtok_item ); /*