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 | daa2cde64601 |
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 { |
