comparison src/libpam/environ.rs @ 100:3f11b8d30f63

Implement environment variable management. This actually wires up the environment variable handling to libpam, so that applications and modules can manage the environment through the authentication process.
author Paul Fisher <paul@pfish.zone>
date Tue, 24 Jun 2025 17:08:01 -0400
parents b87100c5eed4
children dfcd96a74ac4
comparison
equal deleted inserted replaced
99:8840fa6534f6 100:3f11b8d30f63
1 #![allow(unused_variables)] // for now 1 #![allow(unused_variables)] // for now
2 use crate::environ::EnvironMapMut; 2 use crate::constants::{ErrorCode, Result};
3 use crate::environ::{EnvironMap, EnvironMapMut};
3 use crate::libpam::memory::CHeapString; 4 use crate::libpam::memory::CHeapString;
4 use crate::{EnvironMap, LibPamHandle}; 5 use crate::libpam::{memory, pam_ffi, LibPamHandle};
5 use std::ffi::{c_char, OsStr, OsString}; 6 use std::ffi::{c_char, CStr, CString, OsStr, OsString};
6 use std::os::unix::ffi::OsStrExt; 7 use std::marker::PhantomData;
8 use std::os::unix::ffi::{OsStrExt, OsStringExt};
9 use std::ptr;
7 use std::ptr::NonNull; 10 use std::ptr::NonNull;
8 use std::{iter, ptr};
9 use crate::libpam::memory;
10 11
11 pub struct LibPamEnviron<'a> { 12 pub struct LibPamEnviron<'a> {
12 source: &'a LibPamHandle, 13 source: &'a LibPamHandle,
13 } 14 }
14 15
15 pub struct LibPamEnvironMut<'a> { 16 pub struct LibPamEnvironMut<'a> {
16 source: &'a mut LibPamHandle, 17 source: &'a mut LibPamHandle,
18 }
19
20 impl LibPamHandle {
21 fn environ_get(&self, key: &OsStr) -> Option<OsString> {
22 let key = CString::new(key.as_bytes()).ok()?;
23 // SAFETY: We are a valid handle and are calling with a good key.
24 unsafe {
25 copy_env(pam_ffi::pam_getenv(
26 (self as *const LibPamHandle).cast_mut(),
27 key.as_ptr(),
28 ))
29 }
30 }
31
32 fn environ_set(&mut self, key: &OsStr, value: Option<&OsStr>) -> Result<Option<OsString>> {
33 let old = self.environ_get(key);
34 let total_len = key.len() + value.map(OsStr::len).unwrap_or_default() + 2;
35 let mut result = Vec::with_capacity(total_len);
36 result.extend(key.as_bytes());
37 if let Some(value) = value {
38 result.push(b'=');
39 result.extend(value.as_bytes());
40 }
41 let put = CString::new(result).map_err(|_| ErrorCode::ConversationError)?;
42 // SAFETY: This is a valid handle and a valid environment string.
43 ErrorCode::result_from(unsafe { pam_ffi::pam_putenv(self, put.as_ptr()) })?;
44 Ok(old)
45 }
46
47 fn environ_iter(&self) -> Result<impl Iterator<Item = (OsString, OsString)>> {
48 // SAFETY: This is a valid PAM handle.
49 unsafe {
50 NonNull::new(pam_ffi::pam_getenvlist(
51 (self as *const LibPamHandle).cast_mut(),
52 ))
53 .map(|ptr| EnvList::from_ptr(ptr.cast()))
54 .ok_or(ErrorCode::BufferError)
55 }
56 }
57 }
58
59 /// Copies the data of the given C string pointer to an OsString,
60 /// or None if src is null.
61 unsafe fn copy_env(src: *const c_char) -> Option<OsString> {
62 let val = match NonNull::new(src.cast_mut()) {
63 None => return None,
64 Some(ptr) => ptr.as_ptr(),
65 };
66 // SAFETY: We were just returned this string from PAM.
67 // We have to trust it.
68 let c_str = unsafe { CStr::from_ptr(val) };
69 Some(OsString::from_vec(c_str.to_bytes().to_vec()))
17 } 70 }
18 71
19 impl<'a> LibPamEnviron<'a> { 72 impl<'a> LibPamEnviron<'a> {
20 pub fn new(source: &'a LibPamHandle) -> Self { 73 pub fn new(source: &'a LibPamHandle) -> Self {
21 Self { source } 74 Self { source }
24 77
25 impl<'a> LibPamEnvironMut<'a> { 78 impl<'a> LibPamEnvironMut<'a> {
26 pub fn new(source: &'a mut LibPamHandle) -> Self { 79 pub fn new(source: &'a mut LibPamHandle) -> Self {
27 Self { source } 80 Self { source }
28 } 81 }
29 82 }
30 fn immut(&self) -> LibPamEnviron { 83
31 LibPamEnviron { 84 impl EnvironMap<'_> for LibPamEnviron<'_> {
32 source: self.source, 85 fn get(&self, key: impl AsRef<OsStr>) -> Option<OsString> {
33 } 86 self.source.environ_get(key.as_ref())
34 } 87 }
35 } 88
36 89 fn iter(&self) -> Result<impl Iterator<Item = (OsString, OsString)>> {
37 impl EnvironMap for LibPamEnviron<'_> { 90 self.source.environ_iter()
38 fn get(&self, val: &OsStr) -> Option<&OsStr> { 91 }
39 todo!() 92 }
40 } 93
41 94 impl EnvironMap<'_> for LibPamEnvironMut<'_> {
42 fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> { 95 fn get(&self, key: impl AsRef<OsStr>) -> Option<OsString> {
43 iter::from_fn(|| todo!()) 96 self.source.environ_get(key.as_ref())
44 } 97 }
45 } 98
46 99 fn iter(&self) -> Result<impl Iterator<Item = (OsString, OsString)>> {
47 impl EnvironMap for LibPamEnvironMut<'_> { 100 self.source.environ_iter()
48 fn get(&self, val: &OsStr) -> Option<&OsStr> { 101 }
49 todo!() 102 }
50 } 103
51 104 impl EnvironMapMut<'_> for LibPamEnvironMut<'_> {
52 fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> { 105 fn insert(
53 iter::from_fn(|| todo!()) 106 &mut self,
54 } 107 key: impl AsRef<OsStr>,
55 } 108 val: impl AsRef<OsStr>,
56 109 ) -> Result<Option<OsString>> {
57 impl EnvironMapMut for LibPamEnvironMut<'_> { 110 self.source.environ_set(key.as_ref(), Some(val.as_ref()))
58 fn insert(&mut self, key: &OsStr, val: &OsStr) -> Option<OsString> { 111 }
59 todo!() 112
60 } 113 fn remove(&mut self, key: impl AsRef<OsStr>) -> Result<Option<OsString>> {
61 114 self.source.environ_set(key.as_ref(), None)
62 fn remove(&mut self, key: &OsStr) -> Option<OsString> { 115 }
63 todo!() 116 }
64 } 117
65 } 118 struct EnvList<'a> {
66
67 struct EnvList {
68 /// Pointer to the start of the environment variable list. 119 /// Pointer to the start of the environment variable list.
69 /// 120 ///
70 /// This can't be a `CHeapBox` because it's not just a single 121 /// This can't be a `CHeapBox` because it's not just a single
71 /// `Option<EnvVar>`. 122 /// `Option<EnvVar>`.
72 vars: NonNull<Option<EnvVar>>, 123 start: NonNull<Option<EnvVar>>,
73 } 124 /// The environment variable we're about to iterate into.
74 125 current: NonNull<Option<EnvVar>>,
75 impl EnvList { 126 _owner: PhantomData<&'a LibPamHandle>,
127 }
128
129 impl EnvList<'_> {
76 unsafe fn from_ptr(ptr: NonNull<*mut c_char>) -> Self { 130 unsafe fn from_ptr(ptr: NonNull<*mut c_char>) -> Self {
77 Self{vars: ptr.cast()} 131 Self {
78 } 132 start: ptr.cast(),
79 133 current: ptr.cast(),
80 fn iter(&self) -> impl Iterator<Item = (&OsStr, &OsStr)> { 134 _owner: Default::default(),
81 let mut current = self.vars; 135 }
82 iter::from_fn(move || { 136 }
83 match unsafe {current.as_ref()} { 137 }
84 None => None, 138 impl Iterator for EnvList<'_> {
85 Some(item) => { 139 type Item = (OsString, OsString);
86 let ret = item.as_kv(); 140
87 current = unsafe {current.add(1) }; 141 fn next(&mut self) -> Option<Self::Item> {
88 Some(ret) 142 // SAFETY: We were given a pointer to a valid environment list,
143 // and we only ever advance it to the exact end of the list.
144 match unsafe { self.current.as_mut() } {
145 None => None,
146 Some(item) => {
147 let ret = item.as_kv();
148 // SAFETY: We know we're still pointing to a valid pointer,
149 // and advancing it one more is allowed.
150 unsafe {
151 self.current = self.current.add(1);
152 ptr::drop_in_place(item as *mut EnvVar);
89 } 153 }
154 Some(ret)
90 } 155 }
91 }) 156 }
92 } 157 }
93 } 158 }
94 159
95 impl Drop for EnvList { 160 impl Drop for EnvList<'_> {
96 fn drop(&mut self) { 161 fn drop(&mut self) {
97 // SAFETY: We own this pointer, and we know it's valid environment data 162 // SAFETY: We own self.start, and we know that self.current points to
98 // from PAM. 163 // either an item we haven't used, or to the None end.
99 unsafe { 164 unsafe {
100 let mut var = self.vars; 165 while let Some(var_ref) = self.current.as_mut() {
101 while let Some(var_ref) = var.as_mut() {
102 ptr::drop_in_place(var_ref as *mut EnvVar); 166 ptr::drop_in_place(var_ref as *mut EnvVar);
103 var = var.add(1); 167 self.current = self.current.add(1);
104 } 168 }
105 memory::free(self.vars.as_ptr()) 169 memory::free(self.start.as_ptr())
106 } 170 }
107 } 171 }
108 } 172 }
109 173
110 struct EnvVar(CHeapString); 174 struct EnvVar(CHeapString);
111 175
112 impl EnvVar { 176 impl EnvVar {
113 fn as_kv(&self) -> (&OsStr, &OsStr) { 177 fn as_kv(&self) -> (OsString, OsString) {
114 let bytes = self.0.to_bytes(); 178 let bytes = self.0.to_bytes();
115 let mut split = bytes.splitn(2, |&b| b == b'='); 179 let mut split = bytes.splitn(2, |&b| b == b'=');
116 ( 180 (
117 OsStr::from_bytes(split.next().unwrap_or_default()), 181 OsString::from_vec(split.next().unwrap_or_default().into()),
118 OsStr::from_bytes(split.next().unwrap_or_default()), 182 OsString::from_vec(split.next().unwrap_or_default().into()),
119 ) 183 )
120 } 184 }
121 } 185 }
122 186
123 #[cfg(test)] 187 #[cfg(test)]
124 mod tests { 188 mod tests {
125 use crate::libpam::memory::CHeapBox;
126 use super::*; 189 use super::*;
190
191 fn os(text: &str) -> OsString {
192 OsString::from_vec(text.into())
193 }
127 194
128 #[test] 195 #[test]
129 fn test_split_kv() { 196 fn test_split_kv() {
130 fn test(input: &str, key: &str, value: &str) { 197 fn test(input: &str, key: &str, value: &str) {
131 let data = CHeapString::new(input).unwrap(); 198 let data = CHeapString::new(input).unwrap();
132 let key = OsStr::from_bytes(key.as_bytes()); 199 let key = os(key);
133 let value = OsStr::from_bytes(value.as_bytes()); 200 let value = os(value);
134 201
135 assert_eq!(EnvVar(data).as_kv(), (key, value)); 202 assert_eq!(EnvVar(data).as_kv(), (key, value));
136 } 203 }
137 test("THIS=that", "THIS", "that"); 204 test("THIS=that", "THIS", "that");
138 test("THESE=those, no one=knows", "THESE", "those, no one=knows"); 205 test("THESE=those, no one=knows", "THESE", "those, no one=knows");
139 test("HERE=", "HERE", ""); 206 test("HERE=", "HERE", "");
140 test("SOME", "SOME", ""); 207 test("SOME", "SOME", "");
141 test("", "", ""); 208 test("", "", "");
142 } 209 }
143 210
211 fn env_list(strings: &[&'static str]) -> EnvList<'static> {
212 let ptrs: NonNull<Option<CHeapString>> = memory::calloc(strings.len() + 1).unwrap();
213 unsafe {
214 for (idx, &text) in strings.iter().enumerate() {
215 ptr::write(
216 ptrs.add(idx).as_ptr(),
217 Some(CHeapString::new(text).unwrap()),
218 )
219 }
220 ptr::write(ptrs.add(strings.len()).as_ptr(), None);
221 EnvList::from_ptr(ptrs.cast())
222 }
223 }
224
144 #[test] 225 #[test]
145 fn test_iter() { 226 fn test_iter() {
146 let bx = CHeapBox::new([ 227 let envs = env_list(&["ONE=two", "BIRDS=birds=birds", "me", "you="]);
147 Some("ONE=two"), 228 let result: Vec<_> = envs.collect();
148 Some("BIRDS=birds=birds"), 229 assert_eq!(
149 Some("me"), 230 vec![
150 Some("you="), 231 (os("ONE"), os("two")),
151 None, 232 (os("BIRDS"), os("birds=birds")),
152 ].map(|txt| txt.map(|txt| CHeapString::new(txt).unwrap()))).unwrap(); 233 (os("me"), os("")),
153 let env_ptr = CHeapBox::into_ptr(bx); 234 (os("you"), os("")),
154 235 ],
155 let envs = unsafe {EnvList::from_ptr(env_ptr.cast())}; 236 result
156 let bytes = |data: &'static str| OsStr::from_bytes(data.as_ref()); 237 );
157 let result: Vec<_> = envs.iter().collect(); 238 }
158 assert_eq!(vec![ 239
159 (bytes("ONE"), bytes("two")), 240 #[test]
160 (bytes("BIRDS"), bytes("birds=birds")), 241 fn test_iter_partial() {
161 (bytes("me"), bytes("")), 242 let mut envs = env_list(&[
162 (bytes("you"), bytes("")), 243 "iterating=this",
163 ], 244 "also=here",
164 result); 245 "but not=this one",
165 } 246 "or even=the last",
166 } 247 ]);
248
249 assert_eq!(Some((os("iterating"), os("this"))), envs.next());
250 assert_eq!(Some((os("also"), os("here"))), envs.next());
251 // let envs drop
252 }
253 }