Mercurial > crates > nonstick
comparison src/module.rs @ 51:9d1160b02d2c
Safety and doc fixes:
- Don't panic when given a string with a null character;
instead return `PAM_CONV_ERR`.
- Improve pattern matching and use ?s where appropriate.
- Format etc.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sat, 03 May 2025 18:41:25 -0400 |
parents | a921b72743e4 |
children |
comparison
equal
deleted
inserted
replaced
50:171fb1171053 | 51:9d1160b02d2c |
---|---|
1 //! Functions for use in pam modules. | 1 //! Functions for use in pam modules. |
2 | 2 |
3 use crate::constants::{PamFlag, PamResultCode}; | |
4 use crate::items::{Item, ItemType}; | |
3 use libc::c_char; | 5 use libc::c_char; |
4 use std::ffi::{CStr, CString}; | 6 use std::ffi::{CStr, CString}; |
5 | |
6 use crate::constants::{PamFlag, PamResultCode}; | |
7 use crate::items::ItemType; | |
8 | 7 |
9 /// Opaque type, used as a pointer when making pam API calls. | 8 /// Opaque type, used as a pointer when making pam API calls. |
10 /// | 9 /// |
11 /// A module is invoked via an external function such as `pam_sm_authenticate`. | 10 /// A module is invoked via an external function such as `pam_sm_authenticate`. |
12 /// Such a call provides a pam handle pointer. The same pointer should be given | 11 /// Such a call provides a pam handle pointer. The same pointer should be given |
83 /// | 82 /// |
84 /// # Safety | 83 /// # Safety |
85 /// | 84 /// |
86 /// The data stored under the provided key must be of type `T` otherwise the | 85 /// The data stored under the provided key must be of type `T` otherwise the |
87 /// behaviour of this function is undefined. | 86 /// behaviour of this function is undefined. |
88 pub unsafe fn get_data<T>(&self, key: &str) -> PamResult<&T> { | 87 /// |
89 let c_key = CString::new(key).unwrap(); | 88 /// The data, if present, is owned by the current PAM conversation. |
89 pub unsafe fn get_data<T>(&self, key: &str) -> PamResult<Option<&T>> { | |
90 let c_key = CString::new(key).map_err(|_| PamResultCode::PAM_CONV_ERR)?; | |
90 let mut ptr: *const libc::c_void = std::ptr::null(); | 91 let mut ptr: *const libc::c_void = std::ptr::null(); |
91 let res = pam_get_data(self, c_key.as_ptr(), &mut ptr); | 92 to_result(pam_get_data(self, c_key.as_ptr(), &mut ptr))?; |
92 if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() { | 93 match ptr.is_null() { |
93 let typed_ptr = ptr.cast::<T>(); | 94 true => Ok(None), |
94 let data: &T = &*typed_ptr; | 95 false => { |
95 Ok(data) | 96 let typed_ptr = ptr.cast::<T>(); |
96 } else { | 97 Ok(Some(&*typed_ptr)) |
97 Err(res) | 98 } |
98 } | 99 } |
99 } | 100 } |
100 | 101 |
101 /// Stores a value that can be retrieved later with `get_data`. The value lives | 102 /// Stores a value that can be retrieved later with `get_data`. |
102 /// as long as the current pam cycle. | 103 /// The conversation takes ownership of the data. |
103 /// | 104 /// |
104 /// See the [`pam_set_data` manual page]( | 105 /// See the [`pam_set_data` manual page]( |
105 /// https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html). | 106 /// https://www.man7.org/linux/man-pages/man3/pam_set_data.3.html). |
106 /// | 107 /// |
107 /// # Errors | 108 /// # Errors |
108 /// | 109 /// |
109 /// Returns an error if the underlying PAM function call fails. | 110 /// Returns an error if the underlying PAM function call fails. |
110 pub fn set_data<T>(&self, key: &str, data: Box<T>) -> PamResult<()> { | 111 pub fn set_data<T>(&mut self, key: &str, data: Box<T>) -> PamResult<()> { |
111 let c_key = CString::new(key).unwrap(); | 112 let c_key = CString::new(key).map_err(|_| PamResultCode::PAM_CONV_ERR)?; |
112 let res = unsafe { | 113 let res = unsafe { |
113 pam_set_data( | 114 pam_set_data( |
114 self, | 115 self, |
115 c_key.as_ptr(), | 116 c_key.as_ptr(), |
116 Box::into_raw(data).cast::<libc::c_void>(), | 117 Box::into_raw(data).cast::<libc::c_void>(), |
118 ) | 119 ) |
119 }; | 120 }; |
120 to_result(res) | 121 to_result(res) |
121 } | 122 } |
122 | 123 |
123 /// Retrieves a value that has been set, possibly by the pam client. This is | 124 /// Retrieves a value that has been set, possibly by the pam client. |
124 /// particularly useful for getting a `PamConv` reference. | 125 /// This is particularly useful for getting a `PamConv` reference. |
126 /// | |
127 /// These items are *references to PAM memory* | |
128 /// which are *owned by the conversation*. | |
125 /// | 129 /// |
126 /// See the [`pam_get_item` manual page]( | 130 /// See the [`pam_get_item` manual page]( |
127 /// https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html). | 131 /// https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html). |
128 /// | 132 /// |
129 /// # Errors | 133 /// # Errors |
130 /// | 134 /// |
131 /// Returns an error if the underlying PAM function call fails. | 135 /// Returns an error if the underlying PAM function call fails. |
132 pub fn get_item<T: crate::items::Item>(&self) -> PamResult<Option<T>> { | 136 pub fn get_item<T: crate::items::Item>(&self) -> PamResult<Option<T>> { |
133 let mut ptr: *const libc::c_void = std::ptr::null(); | 137 let mut ptr: *const libc::c_void = std::ptr::null(); |
134 let (res, item) = unsafe { | 138 let out = unsafe { |
135 let r = pam_get_item(self, T::type_id(), &mut ptr); | 139 let r = pam_get_item(self, T::type_id(), &mut ptr); |
140 to_result(r)?; | |
136 let typed_ptr = ptr.cast::<T::Raw>(); | 141 let typed_ptr = ptr.cast::<T::Raw>(); |
137 let t = if typed_ptr.is_null() { | 142 match typed_ptr.is_null() { |
138 None | 143 true => None, |
139 } else { | 144 false => Some(T::from_raw(typed_ptr)), |
140 Some(T::from_raw(typed_ptr)) | 145 } |
141 }; | |
142 (r, t) | |
143 }; | 146 }; |
144 match res { | 147 Ok(out) |
145 PamResultCode::PAM_SUCCESS => Ok(item), | 148 } |
146 other => Err(other), | 149 |
147 } | 150 /// Sets an item in the pam context. It can be retrieved using `get_item`. |
148 } | |
149 | |
150 /// Sets a value in the pam context. The value can be retrieved using | |
151 /// `get_item`. | |
152 /// | |
153 /// Note that all items are strings, except `PAM_CONV` and `PAM_FAIL_DELAY`. | |
154 /// | 151 /// |
155 /// See the [`pam_set_item` manual page]( | 152 /// See the [`pam_set_item` manual page]( |
156 /// https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html). | 153 /// https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html). |
157 /// | 154 /// |
158 /// # Errors | 155 /// # Errors |
159 /// | 156 /// |
160 /// Returns an error if the underlying PAM function call fails. | 157 /// Returns an error if the underlying PAM function call fails. |
161 /// | 158 pub fn set_item<T: Item>(&mut self, item: T) -> PamResult<()> { |
162 /// # Panics | |
163 /// | |
164 /// Panics if the provided item key contains a nul byte. | |
165 pub fn set_item_str<T: crate::items::Item>(&mut self, item: T) -> PamResult<()> { | |
166 let res = | 159 let res = |
167 unsafe { pam_set_item(self, T::type_id(), item.into_raw().cast::<libc::c_void>()) }; | 160 unsafe { pam_set_item(self, T::type_id(), item.into_raw().cast::<libc::c_void>()) }; |
168 to_result(res) | 161 to_result(res) |
169 } | 162 } |
170 | 163 |
176 /// https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html). | 169 /// https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html). |
177 /// | 170 /// |
178 /// # Errors | 171 /// # Errors |
179 /// | 172 /// |
180 /// Returns an error if the underlying PAM function call fails. | 173 /// Returns an error if the underlying PAM function call fails. |
181 /// | |
182 /// # Panics | |
183 /// | |
184 /// Panics if the provided prompt string contains a nul byte. | |
185 pub fn get_user(&self, prompt: Option<&str>) -> PamResult<String> { | 174 pub fn get_user(&self, prompt: Option<&str>) -> PamResult<String> { |
186 let prompt_string; | 175 let prompt = option_cstr(prompt)?; |
187 let c_prompt = match prompt { | |
188 Some(p) => { | |
189 prompt_string = CString::new(p).unwrap(); | |
190 prompt_string.as_ptr() | |
191 } | |
192 None => std::ptr::null(), | |
193 }; | |
194 let output: *mut c_char = std::ptr::null_mut(); | 176 let output: *mut c_char = std::ptr::null_mut(); |
195 let res = unsafe { pam_get_user(self, &output, c_prompt) }; | 177 let res = unsafe { pam_get_user(self, &output, prompt_ptr(prompt.as_ref())) }; |
196 match res { | 178 match res { |
197 PamResultCode::PAM_SUCCESS => copy_pam_string(output), | 179 PamResultCode::PAM_SUCCESS => copy_pam_string(output), |
198 otherwise => Err(otherwise), | 180 otherwise => Err(otherwise), |
199 } | 181 } |
200 } | 182 } |
207 /// https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html). | 189 /// https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html). |
208 /// | 190 /// |
209 /// # Errors | 191 /// # Errors |
210 /// | 192 /// |
211 /// Returns an error if the underlying PAM function call fails. | 193 /// Returns an error if the underlying PAM function call fails. |
212 /// | |
213 /// # Panics | |
214 /// | |
215 /// Panics if the provided prompt string contains a nul byte. | |
216 pub fn get_authtok(&self, prompt: Option<&str>) -> PamResult<String> { | 194 pub fn get_authtok(&self, prompt: Option<&str>) -> PamResult<String> { |
217 let prompt_string; | 195 let prompt = option_cstr(prompt)?; |
218 let c_prompt = match prompt { | 196 let output: *mut c_char = std::ptr::null_mut(); |
219 Some(p) => { | 197 let res = unsafe { |
220 prompt_string = CString::new(p).unwrap(); | 198 pam_get_authtok( |
221 prompt_string.as_ptr() | 199 self, |
222 } | 200 ItemType::AuthTok, |
223 None => std::ptr::null(), | 201 &output, |
202 prompt_ptr(prompt.as_ref()), | |
203 ) | |
224 }; | 204 }; |
225 let output: *mut c_char = std::ptr::null_mut(); | 205 to_result(res)?; |
226 let res = unsafe { pam_get_authtok(self, ItemType::AuthTok, &output, c_prompt) }; | 206 copy_pam_string(output) |
227 match res { | 207 } |
228 PamResultCode::PAM_SUCCESS => copy_pam_string(output), | 208 } |
229 otherwise => Err(otherwise), | 209 |
230 } | 210 /// Safely converts a `&str` option to a `CString` option. |
211 fn option_cstr(prompt: Option<&str>) -> PamResult<Option<CString>> { | |
212 prompt | |
213 .map(CString::new) | |
214 .transpose() | |
215 .map_err(|_| PamResultCode::PAM_CONV_ERR) | |
216 } | |
217 | |
218 /// The pointer to the prompt CString, or null if absent. | |
219 fn prompt_ptr(prompt: Option<&CString>) -> *const c_char { | |
220 match prompt { | |
221 Some(c_str) => c_str.as_ptr(), | |
222 None => std::ptr::null(), | |
231 } | 223 } |
232 } | 224 } |
233 | 225 |
234 /// Creates an owned copy of a string that is returned from a | 226 /// Creates an owned copy of a string that is returned from a |
235 /// <code>pam_get_<var>whatever</var></code> function. | 227 /// <code>pam_get_<var>whatever</var></code> function. |
236 fn copy_pam_string(result_ptr: *const c_char) -> PamResult<String> { | 228 fn copy_pam_string(result_ptr: *const c_char) -> PamResult<String> { |
237 // We really shouldn't get a null pointer back here, but if we do, return nothing. | 229 // We really shouldn't get a null pointer back here, but if we do, return nothing. |
238 if result_ptr.is_null() { | 230 if result_ptr.is_null() { |
239 return Ok(String::from("")); | 231 return Ok(String::new()); |
240 } | 232 } |
241 let bytes = unsafe { CStr::from_ptr(result_ptr).to_bytes() }; | 233 let bytes = unsafe { CStr::from_ptr(result_ptr) }; |
242 String::from_utf8(bytes.to_vec()).map_err(|_| PamResultCode::PAM_CONV_ERR) | 234 Ok(bytes |
235 .to_str() | |
236 .map_err(|_| PamResultCode::PAM_CONV_ERR)? | |
237 .into()) | |
243 } | 238 } |
244 | 239 |
245 /// Convenience to transform a `PamResultCode` into a unit `PamResult`. | 240 /// Convenience to transform a `PamResultCode` into a unit `PamResult`. |
246 fn to_result(result: PamResultCode) -> PamResult<()> { | 241 fn to_result(result: PamResultCode) -> PamResult<()> { |
247 match result { | 242 match result { |