view src/module.rs @ 74:c7c596e6388f

Make conversations type-safe (last big reorg) (REAL) (NOT CLICKBAIT) In previous versions of Conversation, you could send messages and then return messages of the wrong type or in the wrong order or whatever. The receiver would then have to make sure that there were the right number of messages and that each message was the right type. That's annoying. This change makes the `Message` enum a two-way channel, where the asker puts their question into it, and then the answerer (the conversation) puts the answer in and returns control to the asker. The asker then only has to pull the Answer of the type they wanted out of the message.
author Paul Fisher <paul@pfish.zone>
date Fri, 06 Jun 2025 22:21:17 -0400
parents ac6881304c78
children 002adfb98c5c
line wrap: on
line source

//! Functions and types useful for implementing a PAM module.

// Temporarily allowed until we get the actual conversation functions hooked up.
#![allow(dead_code)]

use crate::constants::{ErrorCode, Flags, Result};
use crate::handle::PamHandleModule;
use std::ffi::CStr;

/// A trait for a PAM module to implement.
///
/// The default implementations of all these hooks tell PAM to ignore them
/// (i.e., behave as if this module does not exist) by returning [`ErrorCode::Ignore`].
/// Override any functions you wish to handle in your module.
/// After implementing this trait, use the [`pam_hooks!`](crate::pam_hooks!) macro
/// to make the functions available to PAM.
///
/// For more information, see [`pam(3)`’s root manual page][manpage]
/// and the [PAM Module Writer’s Guide][mwg].
///
/// [manpage]: https://www.man7.org/linux/man-pages/man3/pam.3.html
/// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/Linux-PAM_MWG.html
#[allow(unused_variables)]
pub trait PamModule<T: PamHandleModule> {
    // Functions for auth modules.

    /// Authenticate the user.
    ///
    /// This is probably the first thing you want to implement.
    /// In most cases, you will want to get the user and password,
    /// using [`PamHandle::get_user`] and [`PamModuleOnly::get_authtok`],
    /// and verify them against something.
    ///
    /// See [the Module Writer's Guide entry for `pam_sm_authenticate`][mwg]
    /// for more information.
    ///
    /// # Valid flags
    ///
    /// This function may be called with the following flags set:
    ///
    /// - [`Flags::SILENT`]
    /// - [`Flags::DISALLOW_NULL_AUTHTOK`]
    ///
    /// # Returns
    ///
    /// If the password check was successful, return `Ok(())`.
    ///
    /// Sensible error codes to return include:
    ///
    /// - [`ErrorCode::AuthenticationError`]: Generic authentication error
    ///   (like an incorrect password).
    /// - [`ErrorCode::CredentialsInsufficient`]: The application does not have
    ///   sufficient credentials to authenticate the user.
    /// - [`ErrorCode::AuthInfoUnavailable`]: The module was not able to access
    ///   the authentication information, for instance due to a network failure.
    /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service.
    /// - [`ErrorCode::MaxTries`]: The user has tried authenticating too many times.
    ///   They should not try again.
    ///
    /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-auth.html#mwg-pam_sm_authenticate
    fn authenticate(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
        Err(ErrorCode::Ignore)
    }

    /// Perform "account management".
    ///
    /// When PAM calls this function, the user has already been authenticated
    /// by an authentication module (either this one or some other module).
    /// This hook can check for other things, for instance:
    ///
    /// - Date/time (keep your kids off the computer at night)
    /// - Remote host (only let employees log in from the office)
    ///
    /// You can also check things like, e.g., password expiration,
    /// and alert that the user change it before continuing,
    /// or really do whatever you want.
    ///
    /// See [the Module Writer's Guide entry for `pam_sm_acct_mgmt`][mwg]
    /// for more information.
    ///
    /// # Valid flags
    ///
    /// This function may be called with the following flags set:
    ///
    /// - [`Flags::SILENT`]
    /// - [`Flags::DISALLOW_NULL_AUTHTOK`]
    ///
    /// # Returns
    ///
    /// If the user should be allowed to log in, return `Ok(())`.
    ///
    /// Sensible error codes to return include:
    ///
    /// - [`ErrorCode::AccountExpired`]: The user's account has expired.
    /// - [`ErrorCode::AuthenticationError`]: Generic authentication error.
    /// - [`ErrorCode::NewAuthTokRequired`]: The user's authentication token has expired.
    ///   PAM will ask the user to set a new authentication token, which may be handled by
    ///   this module in [`Self::change_authtok`].
    /// - [`ErrorCode::PermissionDenied`]: This one is pretty self-explanatory.
    /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service.
    ///
    /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-acct.html#mwg-pam_sm_acct_mgmt
    fn account_management(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
        Err(ErrorCode::Ignore)
    }

    /// Set credentials on this session.
    ///
    /// If an authentication module knows more about the user than just
    /// their authentication token, then it uses this function to provide
    /// that information to the application. It should only be called after
    /// authentication but before a session is established.
    ///
    /// See [the Module Writer's Guide entry for `pam_sm_setcred`][mwg]
    /// for more information.
    ///
    /// # Valid flags
    ///
    /// This function may be called with the following flags set:
    ///
    /// - [`Flags::SILENT`]
    /// - [`Flags::ESTABLISH_CREDENTIALS`]: Initialize credentials for the user.
    /// - [`Flags::DELETE_CREDENTIALS`]: Delete the credentials associated with this module.
    /// - [`Flags::REINITIALIZE_CREDENTIALS`]: Re-initialize credentials for this user.
    /// - [`Flags::REFRESH_CREDENTIALS`]: Extend the lifetime of the user's credentials.
    ///
    /// # Returns
    ///
    /// If credentials were set successfully, return `Ok(())`.
    ///
    /// Sensible error codes to return include:
    ///
    /// - [`ErrorCode::CredentialsUnavailable`]: The credentials cannot be retrieved.
    /// - [`ErrorCode::CredentialsExpired`]: The credentials have expired.
    /// - [`ErrorCode::CredentialsError`]: Some other error occurred when setting credentials.
    /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service.
    ///
    /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-auth.html#mwg-pam_sm_setcred
    fn set_credentials(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
        Err(ErrorCode::Ignore)
    }

    // Function for chauthtok modules.

    /// Called to set or reset the user's authentication token.
    ///
    /// PAM calls this function twice in succession.
    ///  1. The first time, [`Flags::PRELIMINARY_CHECK`] will be set.
    ///     If the new token is acceptable, return success;
    ///     if not, return [`ErrorCode::TryAgain`] to re-prompt the user.
    ///  2. After the preliminary check succeeds, [`Flags::UPDATE_AUTHTOK`]
    ///     will be set. On this call, actually update the stored auth token.
    ///
    /// See [the Module Writer's Guide entry for `pam_sm_chauthtok`][mwg]
    /// for more information.
    ///
    /// # Valid flags
    ///
    /// This function may be called with the following flags set:
    ///
    /// - [`Flags::SILENT`]
    /// - [`Flags::CHANGE_EXPIRED_AUTHTOK`]: This module should only change
    ///   any expired passwords, and leave non-expired passwords alone.
    ///   If present, it _must_ be combined with one of the following.
    /// - [`Flags::PRELIMINARY_CHECK`]: Don't actually change the password,
    ///   just check if the new one is valid.
    /// - [`Flags::UPDATE_AUTHTOK`]: Do actually change the password.
    ///
    /// # Returns
    ///
    /// If the authentication token was changed successfully
    /// (or the check passed), return `Ok(())`.
    ///
    /// Sensible error codes to return include:
    ///
    /// - [`ErrorCode::AuthTokError`]: The service could not get the authentication token.
    /// - [`ErrorCode::AuthTokRecoveryError`]: The service could not get the old token.
    /// - [`ErrorCode::AuthTokLockBusy`]: The password cannot be changed because
    ///   the authentication token is currently locked.
    /// - [`ErrorCode::AuthTokDisableAging`]: Aging (expiration) is disabled.
    /// - [`ErrorCode::PermissionDenied`]: What it says on the tin.
    /// - [`ErrorCode::TryAgain`]: When the preliminary check is unsuccessful,
    ///   ask the user for a new authentication token.
    /// - [`ErrorCode::UserUnknown`]: The supplied username is not known by this service.
    ///
    /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-chauthtok.html#mwg-pam_sm_chauthtok
    fn change_authtok(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
        Err(ErrorCode::Ignore)
    }

    // Functions for session modules.

    /// Called when a session is opened.
    ///
    /// See [the Module Writer's Guide entry for `pam_sm_open_session`][mwg]
    /// for more information.
    ///
    /// # Valid flags
    ///
    /// The only valid flag is [`Flags::SILENT`].
    ///
    /// # Returns
    ///
    /// If the session was opened successfully, return `Ok(())`.
    ///
    /// A sensible error code to return is:
    ///
    /// - [`ErrorCode::SessionError`]: Cannot make an entry for this session.
    ///
    /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-session.html#mwg-pam_sm_open_session
    fn open_session(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
        Err(ErrorCode::Ignore)
    }

    /// Called when a session is being terminated.
    ///
    /// See [the Module Writer's Guide entry for `pam_sm_close_session`][mwg]
    /// for more information.
    ///
    /// # Valid flags
    ///
    /// The only valid flag is [`Flags::SILENT`].
    ///
    /// # Returns
    ///
    /// If the session was closed successfully, return `Ok(())`.
    ///
    /// A sensible error code to return is:
    ///
    /// - [`ErrorCode::SessionError`]: Cannot remove an entry for this session.
    ///
    /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-of-module-session.html#mwg-pam_sm_close_session
    fn close_session(handle: &mut T, args: Vec<&CStr>, flags: Flags) -> Result<()> {
        Err(ErrorCode::Ignore)
    }
}