comparison libpam-sys/src/helpers.rs @ 109:bb465393621f

Minor cleanup and reorg. - Use those nice new macros we just implemented. - Straighten out the macro file. - Move the `BinaryPayload` into `structs.rs`, leaving helpers behind.
author Paul Fisher <paul@pfish.zone>
date Sat, 28 Jun 2025 02:49:35 -0400
parents 49d9e2b5c189
children 2346fd501b7a
comparison
equal deleted inserted replaced
108:e97534be35e3 109:bb465393621f
1 //! This module contains a few non-required helpers to deal with some of the 1 //! This module contains a few non-required helpers to deal with some of the
2 //! more annoying memory-twiddling in the PAM API. 2 //! more annoying memory management in the PAM API.
3 //! 3 use crate::structs::BinaryPayload;
4 //! Using these is optional but you might find it useful. 4 use std::fmt;
5 use std::ptr::NonNull;
5 use std::error::Error; 6 use std::error::Error;
6 use std::marker::{PhantomData, PhantomPinned};
7 use std::mem::ManuallyDrop; 7 use std::mem::ManuallyDrop;
8 use std::ptr::NonNull;
9 use std::{fmt, slice};
10 8
11 /// Error returned when attempting to allocate a buffer that is too big. 9 /// Error returned when attempting to allocate a buffer that is too big.
12 /// 10 ///
13 /// This is specifically used in [`OwnedBinaryPayload`] when you try to allocate 11 /// This is specifically used in [`OwnedBinaryPayload`] when you try to allocate
14 /// a message larger than 2<sup>32</sup> bytes. 12 /// a message larger than 2<sup>32</sup> bytes.
121 max: BinaryPayload::MAX_SIZE, 119 max: BinaryPayload::MAX_SIZE,
122 })?; 120 })?;
123 let total_len = total_len as usize; 121 let total_len = total_len as usize;
124 let mut buf = O::allocate(total_len); 122 let mut buf = O::allocate(total_len);
125 // SAFETY: We just allocated this exact size. 123 // SAFETY: We just allocated this exact size.
126 BinaryPayload::fill(unsafe { &mut buf.as_mut_slice(total_len) }, data_type, data); 124 BinaryPayload::fill(unsafe { buf.as_mut_slice(total_len) }, data_type, data);
127 Ok(Self(buf)) 125 Ok(Self(buf))
128 } 126 }
129 127
130 /// The contents of the buffer. 128 /// The contents of the buffer.
131 pub fn contents(&self) -> (u8, &[u8]) { 129 pub fn contents(&self) -> (u8, &[u8]) {
169 /// 167 ///
170 /// You must provide a valid pointer, allocated by (or equivalent to one 168 /// You must provide a valid pointer, allocated by (or equivalent to one
171 /// allocated by) [`Self::new`]. For instance, passing a pointer allocated 169 /// allocated by) [`Self::new`]. For instance, passing a pointer allocated
172 /// by `malloc` to `OwnedBinaryPayload::<Vec<u8>>::from_ptr` is not allowed. 170 /// by `malloc` to `OwnedBinaryPayload::<Vec<u8>>::from_ptr` is not allowed.
173 pub unsafe fn from_ptr(ptr: NonNull<BinaryPayload>) -> Self { 171 pub unsafe fn from_ptr(ptr: NonNull<BinaryPayload>) -> Self {
174 Self(O::from_ptr(ptr.cast(), ptr.as_ref().total_bytes() as usize)) 172 Self(O::from_ptr(ptr.cast(), ptr.as_ref().total_bytes()))
175 }
176 }
177
178 /// The structure of the "binary message" payload for the `PAM_BINARY_PROMPT`
179 /// extension from Linux-PAM.
180 pub struct BinaryPayload {
181 /// The total length of the message, including this header,
182 /// as a u32 in network byte order (big endian).
183 total_length: [u8; 4],
184 /// A tag used to provide some kind of hint as to what the data is.
185 /// This is not defined by PAM.
186 data_type: u8,
187 /// Where the data itself would start, used as a marker to make this
188 /// not [`Unpin`] (since it is effectively an intrusive data structure
189 /// pointing to immediately after itself).
190 _marker: PhantomData<PhantomPinned>,
191 }
192
193 impl BinaryPayload {
194 /// The most data it's possible to put into a [`BinaryPayload`].
195 pub const MAX_SIZE: usize = (u32::MAX - 5) as usize;
196
197 /// Fills in the provided buffer with the given data.
198 ///
199 /// This uses [`copy_from_slice`](slice::copy_from_slice) internally,
200 /// so `buf` must be exactly 5 bytes longer than `data`, or this function
201 /// will panic.
202 pub fn fill(buf: &mut [u8], data_type: u8, data: &[u8]) {
203 let ptr: *mut Self = buf.as_mut_ptr().cast();
204 // SAFETY: We're given a slice, which always has a nonzero pointer.
205 let me = unsafe { ptr.as_mut().unwrap_unchecked() };
206 me.total_length = u32::to_be_bytes(buf.len() as u32);
207 me.data_type = data_type;
208 buf[5..].copy_from_slice(data)
209 }
210
211 /// The size of the message contained in the buffer.
212 fn len(&self) -> usize {
213 self.total_bytes().saturating_sub(5)
214 }
215
216 /// The total storage needed for the message, including header.
217 pub fn total_bytes(&self) -> usize {
218 u32::from_be_bytes(self.total_length) as usize
219 }
220
221 /// Gets the contents of the BinaryMessage starting at the given location.
222 ///
223 /// # Safety
224 ///
225 /// You must pass in a valid pointer, and ensure that the pointer passed in
226 /// outlives your borrow lifetime `'a`.
227 unsafe fn contents<'a>(ptr: *const Self) -> (u8, &'a [u8]) {
228 let header: &Self = ptr.as_ref().unwrap_unchecked();
229 let typ = header.data_type;
230 (
231 typ,
232 slice::from_raw_parts(ptr.cast::<u8>().offset(5), header.len()),
233 )
234 } 173 }
235 } 174 }
236 175
237 #[cfg(test)] 176 #[cfg(test)]
238 mod tests { 177 mod tests {