diff libpam-sys/src/helpers.rs @ 118:39760dfc9b3b

Detect PAM library based only on system lib; rename minimal lib to XSso. Also formats and assorted other cleanup.
author Paul Fisher <paul@pfish.zone>
date Sun, 29 Jun 2025 20:13:03 -0400
parents 20f7712a6857
children 476a22db8639
line wrap: on
line diff
--- a/libpam-sys/src/helpers.rs	Sun Jun 29 18:48:14 2025 -0400
+++ b/libpam-sys/src/helpers.rs	Sun Jun 29 20:13:03 2025 -0400
@@ -1,10 +1,11 @@
 //! This module contains a few non-required helpers to deal with some of the
 //! more annoying memory management in the PAM API.
-use crate::structs::BinaryPayload;
+
 use std::error::Error;
-use std::fmt;
+use std::marker::{PhantomData, PhantomPinned};
 use std::mem::ManuallyDrop;
 use std::ptr::NonNull;
+use std::{fmt, slice};
 
 /// Error returned when attempting to allocate a buffer that is too big.
 ///
@@ -99,6 +100,77 @@
     }
 }
 
+/// The structure of the "binary message" payload for the `PAM_BINARY_PROMPT`
+/// extension from Linux-PAM.
+pub struct BinaryPayload {
+    /// The total byte size of the message, including this header,
+    /// as a u32 in network byte order (big endian).
+    pub total_bytes_u32be: [u8; 4],
+    /// A tag used to provide some kind of hint as to what the data is.
+    /// Its meaning is undefined.
+    pub data_type: u8,
+    /// Where the data itself would start, used as a marker to make this
+    /// not [`Unpin`] (since it is effectively an intrusive data structure
+    /// pointing to immediately after itself).
+    pub _marker: PhantomData<PhantomPinned>,
+}
+
+impl BinaryPayload {
+    /// The most data it's possible to put into a [`BinaryPayload`].
+    pub const MAX_SIZE: usize = (u32::MAX - 5) as usize;
+
+    /// Fills in the provided buffer with the given data.
+    ///
+    /// This uses [`copy_from_slice`](slice::copy_from_slice) internally,
+    /// so `buf` must be exactly 5 bytes longer than `data`, or this function
+    /// will panic.
+    pub fn fill(buf: &mut [u8], data_type: u8, data: &[u8]) {
+        let ptr: *mut Self = buf.as_mut_ptr().cast();
+        // SAFETY: We're given a slice, which always has a nonzero pointer.
+        let me = unsafe { ptr.as_mut().unwrap_unchecked() };
+        me.total_bytes_u32be = u32::to_be_bytes(buf.len() as u32);
+        me.data_type = data_type;
+        buf[5..].copy_from_slice(data)
+    }
+
+    /// The total storage needed for the message, including header.
+    pub fn total_bytes(&self) -> usize {
+        u32::from_be_bytes(self.total_bytes_u32be) as usize
+    }
+
+    /// Gets the total byte buffer of the BinaryMessage stored at the pointer.
+    ///
+    /// The returned data slice is borrowed from where the pointer points to.
+    ///
+    /// # Safety
+    ///
+    /// - The pointer must point to a valid `BinaryPayload`.
+    /// - The borrowed data must not outlive the pointer's validity.
+    pub unsafe fn buffer_of<'a>(ptr: *const Self) -> &'a [u8] {
+        let header: &Self = ptr.as_ref().unwrap_unchecked();
+        slice::from_raw_parts(ptr.cast(), header.total_bytes().max(5))
+    }
+
+    /// Gets the contents of the BinaryMessage stored at the given pointer.
+    ///
+    /// The returned data slice is borrowed from where the pointer points to.
+    /// This is a cheap operation and doesn't do *any* copying.
+    ///
+    /// We don't take a `&self` reference here because accessing beyond
+    /// the range of the `Self` data (i.e., beyond the 5 bytes of `self`)
+    /// is undefined behavior. Instead, you have to pass a raw pointer
+    /// directly to the data.
+    ///
+    /// # Safety
+    ///
+    /// - The pointer must point to a valid `BinaryPayload`.
+    /// - The borrowed data must not outlive the pointer's validity.
+    pub unsafe fn contents<'a>(ptr: *const Self) -> (u8, &'a [u8]) {
+        let header: &Self = ptr.as_ref().unwrap_unchecked();
+        (header.data_type, &Self::buffer_of(ptr)[5..])
+    }
+}
+
 /// A binary message owned by some storage.
 ///
 /// This is an owned, memory-managed version of [`BinaryPayload`].
@@ -132,9 +204,7 @@
 
     /// The total bytes needed to store this, including the header.
     pub fn total_bytes(&self) -> usize {
-        unsafe {
-            BinaryPayload::buffer_of(self.0.as_ptr().cast()).len()
-        }
+        unsafe { BinaryPayload::buffer_of(self.0.as_ptr().cast()).len() }
     }
 
     /// Unwraps this into the raw storage backing it.