comparison src/libpam/memory.rs @ 77:351bdc13005e

Update the libpam module to work with the new structure.
author Paul Fisher <paul@pfish.zone>
date Sun, 08 Jun 2025 01:03:46 -0400
parents c30811b4afae
children 002adfb98c5c
comparison
equal deleted inserted replaced
76:e58d24849e82 77:351bdc13005e
1 //! Things for dealing with memory. 1 //! Things for dealing with memory.
2 2
3 use crate::ErrorCode; 3 use crate::conv::BorrowedBinaryData;
4 use crate::Result; 4 use crate::Result;
5 use crate::{BinaryData, ErrorCode};
5 use std::ffi::{c_char, c_void, CStr, CString}; 6 use std::ffi::{c_char, c_void, CStr, CString};
6 use std::marker::{PhantomData, PhantomPinned}; 7 use std::marker::{PhantomData, PhantomPinned};
7 use std::result::Result as StdResult;
8 use std::{ptr, slice}; 8 use std::{ptr, slice};
9 9
10 /// Makes whatever it's in not [`Send`], [`Sync`], or [`Unpin`]. 10 /// Makes whatever it's in not [`Send`], [`Sync`], or [`Unpin`].
11 #[repr(C)] 11 #[repr(C)]
12 #[derive(Debug)] 12 #[derive(Debug)]
64 /// 64 ///
65 /// This is like [`CString::new`](std::ffi::CString::new), but: 65 /// This is like [`CString::new`](std::ffi::CString::new), but:
66 /// 66 ///
67 /// - it allocates data on the C heap with [`libc::malloc`]. 67 /// - it allocates data on the C heap with [`libc::malloc`].
68 /// - it doesn't take ownership of the data passed in. 68 /// - it doesn't take ownership of the data passed in.
69 pub fn malloc_str(text: impl AsRef<str>) -> StdResult<*mut c_char, NulError> { 69 pub fn malloc_str(text: &str) -> Result<*mut c_char> {
70 let data = text.as_ref().as_bytes(); 70 let data = text.as_bytes();
71 if let Some(nul) = data.iter().position(|x| *x == 0) { 71 if data.contains(&0) {
72 return Err(NulError(nul)); 72 return Err(ErrorCode::ConversationError);
73 } 73 }
74 unsafe { 74 unsafe {
75 let data_alloc = libc::calloc(data.len() + 1, 1); 75 let data_alloc = libc::calloc(data.len() + 1, 1);
76 libc::memcpy(data_alloc, data.as_ptr().cast(), data.len()); 76 libc::memcpy(data_alloc, data.as_ptr().cast(), data.len());
77 Ok(data_alloc.cast()) 77 Ok(data_alloc.cast())
108 _marker: Immovable, 108 _marker: Immovable,
109 } 109 }
110 110
111 impl CBinaryData { 111 impl CBinaryData {
112 /// Copies the given data to a new BinaryData on the heap. 112 /// Copies the given data to a new BinaryData on the heap.
113 pub fn alloc(source: &[u8], data_type: u8) -> StdResult<*mut CBinaryData, TooBigError> { 113 pub fn alloc(source: &[u8], data_type: u8) -> Result<*mut CBinaryData> {
114 let buffer_size = u32::try_from(source.len() + 5).map_err(|_| TooBigError { 114 let buffer_size =
115 max: (u32::MAX - 5) as usize, 115 u32::try_from(source.len() + 5).map_err(|_| ErrorCode::ConversationError)?;
116 actual: source.len(),
117 })?;
118 // SAFETY: We're only allocating here. 116 // SAFETY: We're only allocating here.
119 let data = unsafe { 117 let data = unsafe {
120 let dest_buffer: *mut CBinaryData = libc::malloc(buffer_size as usize).cast(); 118 let dest_buffer: *mut CBinaryData = libc::malloc(buffer_size as usize).cast();
121 let data = &mut *dest_buffer; 119 let data = &mut *dest_buffer;
122 data.total_length = buffer_size.to_be_bytes(); 120 data.total_length = buffer_size.to_be_bytes();
130 128
131 fn length(&self) -> usize { 129 fn length(&self) -> usize {
132 u32::from_be_bytes(self.total_length).saturating_sub(5) as usize 130 u32::from_be_bytes(self.total_length).saturating_sub(5) as usize
133 } 131 }
134 132
135 pub fn contents(&self) -> &[u8] {
136 unsafe { slice::from_raw_parts(self.data.as_ptr(), self.length()) }
137 }
138 pub fn data_type(&self) -> u8 {
139 self.data_type
140 }
141
142 /// Clears this data and frees it. 133 /// Clears this data and frees it.
143 pub unsafe fn zero_contents(&mut self) { 134 pub unsafe fn zero_contents(&mut self) {
144 let contents = slice::from_raw_parts_mut(self.data.as_mut_ptr(), self.length()); 135 let contents = slice::from_raw_parts_mut(self.data.as_mut_ptr(), self.length());
145 for v in contents { 136 for v in contents {
146 *v = 0 137 *v = 0
148 self.data_type = 0; 139 self.data_type = 0;
149 self.total_length = [0; 4]; 140 self.total_length = [0; 4];
150 } 141 }
151 } 142 }
152 143
153 #[derive(Debug, thiserror::Error)] 144 impl<'a> From<&'a CBinaryData> for BorrowedBinaryData<'a> {
154 #[error("null byte within input at byte {0}")] 145 fn from(value: &'a CBinaryData) -> Self {
155 pub struct NulError(pub usize); 146 BorrowedBinaryData::new(
147 unsafe { slice::from_raw_parts(value.data.as_ptr(), value.length()) },
148 value.data_type,
149 )
150 }
151 }
156 152
157 /// Returned when trying to fit too much data into a binary message. 153 impl From<Option<&'_ CBinaryData>> for BinaryData {
158 #[derive(Debug, thiserror::Error)] 154 fn from(value: Option<&CBinaryData>) -> Self {
159 #[error("cannot create a message of {actual} bytes; maximum is {max}")] 155 value.map(BorrowedBinaryData::from).map(Into::into).unwrap_or_default()
160 pub struct TooBigError { 156 }
161 pub actual: usize,
162 pub max: usize,
163 } 157 }
164 158
165 #[cfg(test)] 159 #[cfg(test)]
166 mod tests { 160 mod tests {
167 use super::{copy_pam_string, malloc_str, option_cstr, prompt_ptr, zero_c_string}; 161 use super::{copy_pam_string, malloc_str, option_cstr, prompt_ptr, zero_c_string};