Mercurial > crates > nonstick
view src/handle.rs @ 98:b87100c5eed4
Start on environment variables, and make pointers nicer.
This starts work on the PAM environment handling, and in so doing,
introduces the CHeapBox and CHeapString structs. These are analogous
to Box and CString, but they're located on the C heap rather than
being Rust-managed memory.
This is because environment variables deal with even more pointers
and it turns out we can lose a lot of manual freeing using homemade
smart pointers.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Tue, 24 Jun 2025 04:25:25 -0400 |
parents | efe2f5f8b5b2 |
children | 3f11b8d30f63 |
line wrap: on
line source
//! The wrapper types and traits for handles into the PAM library. use crate::constants::{Flags, Result}; use crate::conv::Conversation; use crate::environ::{EnvironMap, EnvironMapMut}; use crate::logging::Level; macro_rules! trait_item { ($(#[$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), "`].")] )? /// /// 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<String>>; }; ($(#[$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), "`].")] )? /// /// 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<()>; }; } /// Functionality for both PAM applications and PAM modules. /// /// This base trait includes features of a PAM handle that are available /// to both applications and modules. /// /// You probably want [`LibPamHandle`](crate::libpam::OwnedLibPamHandle). /// This trait is intended to allow creating mock PAM handle types /// to test PAM modules and applications. pub trait PamShared { /// Logs something via this PAM handle. /// /// You probably want to use one of the logging macros, /// like [`error!`], [`warning!`], [`info!`], or [`debug!`]. /// /// In most PAM implementations, this will go to syslog. /// /// # Example /// /// ```no_run /// # use nonstick::{PamShared}; /// # use nonstick::logging::Level; /// # fn _test(pam_hdl: impl PamShared) { /// # let delay_ms = 100; /// # let url = "https://zombo.com"; /// // Usually, instead of calling this manually, just use the macros. /// nonstick::error!(pam_hdl, "something bad happened!"); /// nonstick::warn!(pam_hdl, "loading information took {delay_ms} ms"); /// nonstick::info!(pam_hdl, "using network backend"); /// nonstick::debug!(pam_hdl, "sending GET request to {url}"); /// // But if you really want to, you can call this yourself: /// pam_hdl.log(Level::Warning, "this is unnecessarily verbose"); /// # } /// ``` fn log(&self, level: Level, entry: &str); /// Retrieves the name of the user who is authenticating or logging in. /// /// If the username has previously been obtained, this uses that username; /// otherwise it prompts the user with the first of these that is present: /// /// 1. The prompt string passed to this function. /// 2. The string returned by `get_user_prompt_item`. /// 3. The default prompt, `login: `. /// /// See the [`pam_get_user` manual page][man] /// or [`pam_get_user` in the Module Writer's Guide][mwg]. /// /// # Example /// /// ```no_run /// # use nonstick::PamShared; /// # fn _doc(handle: &mut impl PamShared) -> Result<(), Box<dyn std::error::Error>> { /// // Get the username using the default prompt. /// let user = handle.username(None)?; /// // Get the username using a custom prompt. /// // If this were actually called right after the above, /// // both user and user_2 would have the same value. /// let user_2 = handle.username(Some("who ARE you even???"))?; /// # Ok(()) /// # } /// ``` /// /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user fn username(&mut self, prompt: Option<&str>) -> Result<String>; /// The contents of the environment to set, read-only. fn environ(&self) -> impl EnvironMap; /// A writable version of the environment. fn environ_mut(&mut self) -> impl EnvironMapMut; trait_item!( /// The identity of the user for whom service is being requested. /// /// Unlike [`username`](Self::username), this will simply get /// the current state of the user item, and not request the username. /// While PAM usually sets this automatically in the `username` 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::username ); trait_item!( /// Sets the identity of the logging-in user. /// /// Usually this will be set during the course of /// a [`username`](Self::username) 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 ); trait_item!( /// The service name, which identifies the PAM stack which is used /// to perform authentication. get = service, 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 ); 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" ); 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 ); trait_item!( /// The device path of the TTY being used to log in. /// /// This is the terminal the user is logging in on, /// specified as the full device path (e.g. `/dev/tty0`). /// Very old applications may use this instead of `PAM_XDISPLAY`. get = tty_name, item = "PAM_TTY" ); trait_item!( /// Sets the path to the terminal where the user is logging on. set = set_tty_name, item = "PAM_TTY", 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" ); 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" ); 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" ); 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 ); trait_item!( /// Gets the user's authentication token (e.g., password). /// /// This is usually set automatically when /// [`authtok`](PamHandleModule::authtok) is called, /// but can be manually set. set = set_authtok_item, item = "PAM_AUTHTOK", 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 = PamHandleModule::old_authtok_item ); } /// Functionality of a PAM handle that can be expected by a PAM application. /// /// If you are not writing a PAM client application (e.g., you are writing /// a module), you should not use the functionality exposed by this trait. /// /// Like [`PamShared`], this is intended to allow creating mock implementations /// of PAM for testing PAM applications. pub trait PamHandleApplication: PamShared { /// Starts the authentication process for the user. fn authenticate(&mut self, flags: Flags) -> Result<()>; /// Does "account management". fn account_management(&mut self, flags: Flags) -> Result<()>; /// Changes the authentication token. fn change_authtok(&mut self, flags: Flags) -> Result<()>; } /// Functionality of a PAM handle that can be expected by a PAM module. /// /// If you are not writing a PAM module (e.g., you are writing an application), /// you should not use any of the functionality exposed by this trait. /// /// Like [`PamShared`], this is intended to allow creating mock implementations /// of PAM for testing PAM modules. pub trait PamHandleModule: Conversation + PamShared { /// Retrieves the authentication token from the user. /// /// This should only be used by *authentication* and *password-change* /// PAM modules. /// /// See the [`pam_get_authtok` manual page][man] /// or [`pam_get_item` in the Module Writer's Guide][mwg]. /// /// # Example /// /// ```no_run /// # 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.authtok(None)?; /// // Get the user's password using a custom prompt. /// let pass = handle.authtok(Some("Reveal your secrets!"))?; /// Ok(()) /// # } /// ``` /// /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item fn authtok(&mut self, prompt: Option<&str>) -> Result<String>; trait_item!( /// Gets the user's authentication token (e.g., password). /// /// This is normally set automatically by PAM when calling /// [`authtok`](Self::authtok), but can be set explicitly. /// /// Like `authtok`, this should only ever be called /// by *authentication* and *password-change* PAM modules. get = authtok_item, item = "PAM_AUTHTOK", see = Self::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 ); /* TODO: Re-enable this at some point. /// Gets some pointer, identified by `key`, that has been set previously /// using [`set_data`](Self::set_data). /// /// The data, if present, is still owned by the current PAM session. /// /// See the [`pam_get_data` manual page][man] /// or [`pam_get_data` in the Module Writer's Guide][mwg]. /// /// # Safety /// /// The data stored under the provided key must be of type `T`, /// otherwise you'll get back a completely invalid `&T` /// and further behavior is undefined. /// /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_data.3.html /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_data unsafe fn get_data<T>(&mut self, key: &str) -> Result<Option<&T>>; /// Stores a pointer that can be retrieved later with [`get_data`](Self::get_data). /// /// This data is accessible to this module and other PAM modules /// (using the provided `key`), but is *not* accessible to the application. /// The PAM session takes ownership of the data, and it will be dropped /// when the session ends. /// /// See the [`pam_set_data` manual page][man] /// or [`pam_set_data` in the Module Writer's Guide][mwg]. /// /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_data fn set_data<T>(&mut self, key: &str, data: Box<T>) -> Result<()>; */ }