view src/lib.rs @ 183:4f46681b3f54 default tip

Catch a few stray cargo fmt things.
author Paul Fisher <paul@pfish.zone>
date Wed, 30 Jul 2025 18:43:07 -0400
parents 0730f5f2ee2a
children
line wrap: on
line source

//! A safe, nonstick interface to the Pluggable Authentication Module framework.
//!
//! Nonstick provides a fully type- and memory-safe interface to
//! all implementations of PAM, both for PAM modules and PAM applications.
//!
//! # Usage
//!
//! nonstick can be used on either side of a PAM transaction,
//! both to implement an application which calls into PAM,
//! or a module which implements a PAM backend.
//!
//! For more information about how PAM works in general, or more pointers
//! on how to implement a PAM module or application, see the
//! [References](#references) section.
//!
//! ## PAM Application
//!
//! To implement a PAM application, first implement a [`Conversation`],
//! then build a [`Transaction`] with the [`TransactionBuilder`].
//! This can be built into any standard Rust library or binary.
//!
//! ```
//! use nonstick::{
//!     AuthnFlags, Conversation, ConversationAdapter, Result as PamResult, Transaction,
//!     TransactionBuilder,
//! };
//! use std::ffi::{OsStr, OsString};
//!
//! /// A basic Conversation that assumes that any "regular" prompt is for
//! /// the username, and that any "masked" prompt is for the password.
//! ///
//! /// A typical Conversation will provide the user with an interface
//! /// to interact with PAM, e.g. a dialogue box or a terminal prompt.
//! struct UsernamePassConvo {
//!     username: String,
//!     password: String,
//! }
//!
//! // ConversationAdapter is a convenience wrapper for the common case
//! // of only handling one request at a time.
//! impl ConversationAdapter for UsernamePassConvo {
//!     fn prompt(&self, request: impl AsRef<OsStr>) -> PamResult<OsString> {
//!         Ok(OsString::from(&self.username))
//!     }
//!
//!     fn masked_prompt(&self, request: impl AsRef<OsStr>) -> PamResult<OsString> {
//!         Ok(OsString::from(&self.password))
//!     }
//!
//!     fn error_msg(&self, message: impl AsRef<OsStr>) {
//!         // Normally you would want to display this to the user somehow.
//!         // In this case, we're just ignoring it.
//!     }
//!
//!     fn info_msg(&self, message: impl AsRef<OsStr>) {
//!         // ibid.
//!     }
//! }
//!
//! fn authenticate(username: &str, password: &str) -> PamResult<()> {
//!     let user_pass = UsernamePassConvo {
//!         username: username.into(),
//!         password: password.into(),
//!     };
//!
//!     let mut txn = TransactionBuilder::new_with_service("cortex-sso")
//!         .username(username)
//!         .build(user_pass.into_conversation())?;
//!     // If authentication fails, this will return an error.
//!     // We immediately give up rather than re-prompting the user.
//!     txn.authenticate(AuthnFlags::empty())?;
//!     txn.account_management(AuthnFlags::empty())?;
//!     Ok(())
//! }
//! ```
//!
//! PAM just tells you that the user is, in fact, who they say they are.
//! It is up to your application to choose what to do with that information.
//!
//! ## PAM module
//!
//! PAM modules are implemented as dynamic libraries loaded into
//! the address space of the calling application. To implement a module,
//! create a `dylib` crate and implement a [`PamModule`], and export it
//! using the [`pam_export!`] macro.
//! ```toml
//! ## Your Cargo.toml
//! [package]
//! name = "example-package"
//! ## ...
//!
//! [lib]
//! crate-type = ["cdylib"]
//! ```
//! ```
//! // Your lib.rs
//!
//! use nonstick::{
//!     pam_export, AuthnFlags, ErrorCode, ModuleClient, PamModule, Result as PamResult,
//! };
//! use std::ffi::CStr;
//!
//! # // This needs to be here to make this doc example work.
//! # fn main() {}
//!
//! /// A module that only allows you to log in if your username
//! /// is the same as your password.
//! struct SameName;
//! pam_export!(SameName);
//!
//! impl<M: ModuleClient> PamModule<M> for SameName {
//!     fn authenticate(handle: &mut M, _args: Vec<&CStr>, _flags: AuthnFlags) -> PamResult<()> {
//!         // Using `None` as the prompt parameter here will tell PAM
//!         // to use the default prompt.
//!         let username = handle.username(None)?;
//!         let password = handle.authtok(None)?;
//!         if username == password {
//!             Ok(())
//!         } else {
//!             Err(ErrorCode::AuthenticationError)
//!         }
//!     }
//!
//!     // You can implement other methods of PamModule to provide additional
//!     // features.
//! }
//! ```
//!
//! This gets built into a library like `pam_samename.so`. By installing this
//! into your PAM library directory and configuring PAM to use it in
//! the authentication stack (beyond the scope of this documentation), it will
//! be used to authenticate users.
//!
//! # Configuration
//!
//! There are a few different PAM implementations available. By default,
//! nonstick detects which implementation it should use for the current target.
//! If you need to choose a different implementation, set the `LIBPAMSYS_IMPL`
//! environment variable at build time. See the [`libpam_sys`] documentation.
#![doc = concat!("This documentation was built for **", pam_impl_name!(), "**.")]
//!
//! # References
//!
//! - The Linux-PAM guides provide information for a variety of audiences.
//!   While some of it is specific to Linux-PAM, much of it applies to other
//!   PAM implementations:
//!   - [Application Developers' Guide][adg]
//!   - [Module Writers' Guide][mwg]
//!   - [System Administrators' Guide][sag]
//! - PAM framework man page for developers:
//!   - [Linux-PAM developer man page][man7]
//!   - [OpenPAM developer man page][manbsd]
//!   - [Illumos PAM developer man page][mansun]
//! - PAM framework man page for system administrators:
//!   - [Linux-PAM admin documentation][man7pam8]
//!   - [OpenPAM admin documentation][bsdpam8]
//!   - [Illumos pam.conf documentation][sunpam5]
//! - [The original PAM specification][spec] (mostly of historical interest)
#![doc = ""]
#![doc = crate::_doc::man7!(man7pam8: 8 pam)]
#![doc = crate::_doc::manbsd!(bsdpam8: 8 pam)]
#![doc = crate::_doc::mansun!(sunpam5: 5 "pam.conf")]
#![doc = crate::_doc::stdlinks!(3 pam)]
#![doc = crate::_doc::guide!(adg: "Linux-PAM_ADG.html")]
#![doc = crate::_doc::guide!(mwg: "Linux-PAM_MWG.html")]
#![doc = crate::_doc::guide!(sag: "Linux-PAM_SAG.html")]
#![doc = crate::_doc::xsso!(spec: "toc.htm")]

#[cfg(feature = "link")]
mod _compat_checker {
    macro_rules! feature_check {
        ($feature:literal, pam_impl = ($($pimpl:literal),*)) => {
            #[cfg(all(feature = $feature, not(any($(pam_impl = $pimpl),*))))]
            compile_error!(
                concat!(
                    "The feature '", $feature, "' is only available ",
                    "with these PAM implementations:\n",
                    $("- ", $pimpl, "\n"),*,
                    "The current PAM implementation is:\n\n",
                    "  ", libpam_sys::pam_impl_name!(), "\n\n",
                    "Set the 'LIBPAMSYS_IMPL' environment variable to one of ",
                    "the above PAM implementation names to build ",
                    "for that implementation of PAM."
                )
            );
        }
    }
    feature_check!("linux-pam-ext", pam_impl = ("LinuxPam"));
    feature_check!("basic-ext", pam_impl = ("LinuxPam", "OpenPam"));
    feature_check!("openpam-ext", pam_impl = ("OpenPam"));
    feature_check!("sun-ext", pam_impl = ("Sun"));
}

pub mod constants;
pub mod conv;
pub mod module;

pub mod handle;

mod _doc;
mod environ;
pub mod items;
#[cfg(feature = "link")]
pub mod libpam;
pub mod logging;

#[cfg(feature = "link")]
#[doc(inline)]
pub use crate::libpam::{LibPamHandle, LibPamTransaction, TransactionBuilder};
#[doc(inline)]
pub use crate::{
    constants::{
        AuthnFlags, AuthtokAction, AuthtokFlags, BaseFlags, CredAction, ErrorCode, Result,
    },
    conv::{BinaryData, Conversation, ConversationAdapter},
    environ::{EnvironMap, EnvironMapMut},
    handle::{ModuleClient, PamShared, Transaction},
    module::PamModule,
};
use libpam_sys_impls::pam_impl_name;