view src/items.rs @ 63:a7aa5ca0d00d

Move MessageStyle to conv, the only place it is used.
author Paul Fisher <paul@pfish.zone>
date Wed, 21 May 2025 23:19:43 -0400
parents d83623951070
children
line wrap: on
line source

//! Things that can be gotten with the `pam_get_item` function.

use crate::constants::InvalidEnum;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use std::ffi::{c_int, CStr};

/// Enum identifying what a `pam_get_item` return is.
///
/// Generally, you shouldn’t have to worry about this, and instead
/// just use the various [Item] implementations.
#[derive(FromPrimitive)]
#[repr(i32)]
#[non_exhaustive] // because C could give us anything!
pub enum ItemType {
    /// The PAM service name.
    Service = 1,
    /// The user's login name.
    User = 2,
    /// The TTY name.
    Tty = 3,
    /// The remote host (if applicable).
    RemoteHost = 4,
    /// The conversation struct (not a CStr-based item).
    Conversation = 5,
    /// The authentication token (password).
    AuthTok = 6,
    /// The old authentication token (when changing passwords).
    OldAuthTok = 7,
    /// The remote user's name.
    RemoteUser = 8,
    /// The prompt shown when requesting a username.
    UserPrompt = 9,
    /// App-supplied function to override failure delays.
    FailDelay = 10,
    /// X display name.
    XDisplay = 11,
    /// X server authentication data.
    XAuthData = 12,
    /// The type of `pam_get_authtok`.
    AuthTokType = 13,
}

impl TryFrom<c_int> for ItemType {
    type Error = InvalidEnum<Self>;
    fn try_from(value: c_int) -> Result<Self, Self::Error> {
        Self::from_i32(value).ok_or(value.into())
    }
}

impl From<ItemType> for c_int {
    fn from(val: ItemType) -> Self {
        val as Self
    }
}

/// A type that can be requested by [`PamHandle::get_item`](crate::PamHandle::get_item).
pub trait Item {
    /// The `repr(C)` type that is returned (by pointer) by the underlying `pam_get_item` function.
    /// This memory is owned by the PAM session.
    type Raw;

    /// The [ItemType] corresponding to this Rust type.
    fn type_id() -> ItemType;

    /// The function to convert from the pointer to the C-representation to this safer wrapper type.
    ///
    /// # Safety
    ///
    /// This function assumes the pointer is a valid pointer to a [Self::Raw] instance.
    unsafe fn from_raw(raw: *const Self::Raw) -> Self;

    /// The function to convert from this wrapper type to a C-compatible pointer.
    fn into_raw(self) -> *const Self::Raw;
}

/// Macro to generate PAM [Item]s represented as [CStr]s.
macro_rules! cstr_item {
    ($name:ident) => {
        #[doc = "The [CStr] representation of the "]
        #[doc = concat!("[`", stringify!($name), "`](ItemType::", stringify!($name), ")")]
        #[doc = " [Item]."]
        #[derive(Debug)]
        pub struct $name<'s>(pub &'s CStr);

        impl<'s> std::ops::Deref for $name<'s> {
            type Target = &'s CStr;
            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl<'s> Item for $name<'s> {
            type Raw = libc::c_char;

            fn type_id() -> ItemType {
                ItemType::$name
            }

            unsafe fn from_raw(raw: *const Self::Raw) -> Self {
                Self(std::ffi::CStr::from_ptr(raw))
            }

            fn into_raw(self) -> *const Self::Raw {
                self.0.as_ptr()
            }
        }
    };
}

// Conversation is not included here since it's special.

cstr_item!(Service);
cstr_item!(User);
cstr_item!(Tty);
cstr_item!(RemoteHost);
cstr_item!(AuthTok);
cstr_item!(OldAuthTok);
cstr_item!(RemoteUser);
cstr_item!(UserPrompt);