comparison pam/src/module.rs @ 34:ec70822cbdef

Overhaul
author Andy Caldwell <andrew.caldwell@metaswitch.com>
date Sun, 24 Apr 2022 03:42:11 +0100
parents 4263c1d83d5b
children 50371046c61a
comparison
equal deleted inserted replaced
32:ea5f195f035f 34:ec70822cbdef
1 //! Functions for use in pam modules. 1 //! Functions for use in pam modules.
2 2
3 use libc::c_char; 3 use libc::c_char;
4 use std::{mem, ptr};
5 use std::ffi::{CStr, CString}; 4 use std::ffi::{CStr, CString};
6 5
7 use constants::{PamResultCode, PamItemType, PamFlag}; 6 use constants::{PamFlag, PamResultCode};
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
13 /// as an argument when making API calls. 12 /// as an argument when making API calls.
14 #[allow(missing_copy_implementations)] 13 #[repr(C)]
15 pub enum PamHandle {} 14 pub struct PamHandle {
16 15 _data: [u8; 0],
17 #[allow(missing_copy_implementations)] 16 }
18 enum PamItemT {}
19
20 #[allow(missing_copy_implementations)]
21 pub enum PamDataT {}
22 17
23 #[link(name = "pam")] 18 #[link(name = "pam")]
24 extern "C" { 19 extern "C" {
25 fn pam_get_data(pamh: *const PamHandle, 20 fn pam_get_data(
26 module_data_name: *const c_char, 21 pamh: *const PamHandle,
27 data: &mut *const PamDataT) 22 module_data_name: *const c_char,
28 -> PamResultCode; 23 data: &mut *const libc::c_void,
29 24 ) -> PamResultCode;
30 fn pam_set_data(pamh: *const PamHandle, 25
31 module_data_name: *const c_char, 26 fn pam_set_data(
32 data: Box<PamDataT>, 27 pamh: *const PamHandle,
33 cleanup: extern "C" fn(pamh: *const PamHandle, 28 module_data_name: *const c_char,
34 data: Box<PamDataT>, 29 data: *mut libc::c_void,
35 error_status: PamResultCode)) 30 cleanup: extern "C" fn(
36 -> PamResultCode; 31 pamh: *const PamHandle,
37 32 data: *mut libc::c_void,
38 fn pam_get_item(pamh: *const PamHandle, 33 error_status: PamResultCode,
39 item_type: PamItemType, 34 ),
40 item: &mut *const PamItemT) 35 ) -> PamResultCode;
41 -> PamResultCode; 36
42 37 fn pam_get_item(
43 fn pam_set_item(pamh: *mut PamHandle, 38 pamh: *const PamHandle,
44 item_type: PamItemType, 39 item_type: crate::items::ItemType,
45 item: &PamItemT) 40 item: &mut *const libc::c_void,
46 -> PamResultCode; 41 ) -> PamResultCode;
47 42
48 fn pam_get_user(pamh: *const PamHandle, 43 fn pam_set_item(
49 user: &*mut c_char, 44 pamh: *mut PamHandle,
50 prompt: *const c_char) 45 item_type: crate::items::ItemType,
51 -> PamResultCode; 46 item: *const libc::c_void,
52 } 47 ) -> PamResultCode;
53 48
54 #[no_mangle] 49 fn pam_get_user(
55 pub extern "C" fn cleanup<T>(_: *const PamHandle, c_data: Box<PamDataT>, _: PamResultCode) { 50 pamh: *const PamHandle,
51 user: &*mut c_char,
52 prompt: *const c_char,
53 ) -> PamResultCode;
54 }
55
56 pub extern "C" fn cleanup<T>(_: *const PamHandle, c_data: *mut libc::c_void, _: PamResultCode) {
56 unsafe { 57 unsafe {
57 let data: Box<T> = mem::transmute(c_data); 58 let _data: Box<T> = Box::from_raw(c_data.cast::<T>());
58 mem::drop(data);
59 } 59 }
60 } 60 }
61 61
62 pub type PamResult<T> = Result<T, PamResultCode>; 62 pub type PamResult<T> = Result<T, PamResultCode>;
63
64 /// Type-level mapping for safely retrieving values with `get_item`.
65 ///
66 /// See `pam_get_item` in
67 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
68 pub trait PamItem {
69 /// Maps a Rust type to a pam constant.
70 ///
71 /// For example, the type PamConv maps to the constant PAM_CONV. The pam
72 /// API contract specifies that when the API function `pam_get_item` is
73 /// called with the constant PAM_CONV, it will return a value of type
74 /// `PamConv`.
75 fn item_type() -> PamItemType;
76 }
77
78 63
79 impl PamHandle { 64 impl PamHandle {
80 /// Gets some value, identified by `key`, that has been set by the module 65 /// Gets some value, identified by `key`, that has been set by the module
81 /// previously. 66 /// previously.
82 /// 67 ///
83 /// See `pam_get_data` in 68 /// See `pam_get_data` in
84 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html 69 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
70 ///
71 /// # Errors
72 ///
73 /// Returns an error if the underlying PAM function call fails.
74 ///
75 /// # Safety
76 ///
77 /// The data stored under the provided key must be of type `T` otherwise the
78 /// behaviour of this funtion is undefined.
85 pub unsafe fn get_data<'a, T>(&'a self, key: &str) -> PamResult<&'a T> { 79 pub unsafe fn get_data<'a, T>(&'a self, key: &str) -> PamResult<&'a T> {
86 let c_key = CString::new(key).unwrap().as_ptr(); 80 let c_key = CString::new(key).unwrap();
87 let mut ptr: *const PamDataT = ptr::null(); 81 let mut ptr: *const libc::c_void = std::ptr::null();
88 let res = pam_get_data(self, c_key, &mut ptr); 82 let res = pam_get_data(self, c_key.as_ptr(), &mut ptr);
89 if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() { 83 if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
90 let typed_ptr: *const T = mem::transmute(ptr); 84 let typed_ptr = ptr.cast::<T>();
91 let data: &T = &*typed_ptr; 85 let data: &T = &*typed_ptr;
92 Ok(data) 86 Ok(data)
93 } else { 87 } else {
94 Err(res) 88 Err(res)
95 } 89 }
98 /// Stores a value that can be retrieved later with `get_data`. The value lives 92 /// Stores a value that can be retrieved later with `get_data`. The value lives
99 /// as long as the current pam cycle. 93 /// as long as the current pam cycle.
100 /// 94 ///
101 /// See `pam_set_data` in 95 /// See `pam_set_data` in
102 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html 96 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
97 ///
98 /// # Errors
99 ///
100 /// Returns an error if the underlying PAM function call fails.
103 pub fn set_data<T>(&self, key: &str, data: Box<T>) -> PamResult<()> { 101 pub fn set_data<T>(&self, key: &str, data: Box<T>) -> PamResult<()> {
104 let c_key = CString::new(key).unwrap().as_ptr(); 102 let c_key = CString::new(key).unwrap();
105 let res = unsafe { 103 let res = unsafe {
106 let c_data: Box<PamDataT> = mem::transmute(data); 104 pam_set_data(
107 pam_set_data(self, c_key, c_data, cleanup::<T>) 105 self,
106 c_key.as_ptr(),
107 Box::into_raw(data).cast::<libc::c_void>(),
108 cleanup::<T>,
109 )
108 }; 110 };
109 if PamResultCode::PAM_SUCCESS == res { 111 if PamResultCode::PAM_SUCCESS == res {
110 Ok(()) 112 Ok(())
111 } else { 113 } else {
112 Err(res) 114 Err(res)
113 } 115 }
114 } 116 }
115 117
116
117
118 /// Retrieves a value that has been set, possibly by the pam client. This is 118 /// Retrieves a value that has been set, possibly by the pam client. This is
119 /// particularly useful for getting a `PamConv` reference. 119 /// particularly useful for getting a `PamConv` reference.
120 /// 120 ///
121 /// See `pam_get_item` in 121 /// See `pam_get_item` in
122 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html 122 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
123 pub fn get_item<'a, T: PamItem>(&self) -> PamResult<&'a T> { 123 ///
124 let mut ptr: *const PamItemT = ptr::null(); 124 /// # Errors
125 ///
126 /// Returns an error if the underlying PAM function call fails.
127 pub fn get_item<T: crate::items::Item>(&self) -> PamResult<Option<T>> {
128 let mut ptr: *const libc::c_void = std::ptr::null();
125 let (res, item) = unsafe { 129 let (res, item) = unsafe {
126 let r = pam_get_item(self, T::item_type(), &mut ptr); 130 let r = pam_get_item(self, T::type_id(), &mut ptr);
127 let typed_ptr: *const T = mem::transmute(ptr); 131 let typed_ptr = ptr.cast::<T::Raw>();
128 let t: &T = &*typed_ptr; 132 let t = if typed_ptr.is_null() {
133 None
134 } else {
135 Some(T::from_raw(typed_ptr))
136 };
129 (r, t) 137 (r, t)
130 }; 138 };
131 if PamResultCode::PAM_SUCCESS == res { 139 if PamResultCode::PAM_SUCCESS == res {
132 Ok(item) 140 Ok(item)
133 } else { 141 } else {
140 /// 148 ///
141 /// Note that all items are strings, except `PAM_CONV` and `PAM_FAIL_DELAY`. 149 /// Note that all items are strings, except `PAM_CONV` and `PAM_FAIL_DELAY`.
142 /// 150 ///
143 /// See `pam_set_item` in 151 /// See `pam_set_item` in
144 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html 152 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
145 pub fn set_item_str<T: PamItem>(&mut self, item: &str) -> PamResult<()> { 153 ///
146 let c_item = CString::new(item).unwrap().as_ptr(); 154 /// # Errors
147 155 ///
148 let res = unsafe { 156 /// Returns an error if the underlying PAM function call fails.
149 pam_set_item(self, 157 ///
150 T::item_type(), 158 /// # Panics
151 159 ///
152 // unwrapping is okay here, as c_item will not be a NULL 160 /// Panics if the provided item key contains a nul byte
153 // pointer 161 pub fn set_item_str<T: crate::items::Item>(&mut self, item: T) -> PamResult<()> {
154 (c_item as *const PamItemT).as_ref().unwrap()) 162 let res =
155 }; 163 unsafe { pam_set_item(self, T::type_id(), item.into_raw().cast::<libc::c_void>())};
156 if PamResultCode::PAM_SUCCESS == res { 164 if PamResultCode::PAM_SUCCESS == res {
157 Ok(()) 165 Ok(())
158 } else { 166 } else {
159 Err(res) 167 Err(res)
160 } 168 }
164 /// 172 ///
165 /// This is really a specialization of `get_item`. 173 /// This is really a specialization of `get_item`.
166 /// 174 ///
167 /// See `pam_get_user` in 175 /// See `pam_get_user` in
168 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html 176 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
177 ///
178 /// # Errors
179 ///
180 /// 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
169 pub fn get_user(&self, prompt: Option<&str>) -> PamResult<String> { 185 pub fn get_user(&self, prompt: Option<&str>) -> PamResult<String> {
170 let ptr: *mut c_char = ptr::null_mut(); 186 let ptr: *mut c_char = std::ptr::null_mut();
187 let prompt_string;
171 let c_prompt = match prompt { 188 let c_prompt = match prompt {
172 Some(p) => CString::new(p).unwrap().as_ptr(), 189 Some(p) => {
173 None => ptr::null(), 190 prompt_string = CString::new(p).unwrap();
191 prompt_string.as_ptr()
192 }
193 None => std::ptr::null(),
174 }; 194 };
175 let res = unsafe { pam_get_user(self, &ptr, c_prompt) }; 195 let res = unsafe { pam_get_user(self, &ptr, c_prompt) };
176 if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() { 196 if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
177 let const_ptr = ptr as *const c_char; 197 let const_ptr = ptr as *const c_char;
178 let bytes = unsafe { CStr::from_ptr(const_ptr).to_bytes() }; 198 let bytes = unsafe { CStr::from_ptr(const_ptr).to_bytes() };
193 /// This function performs the task of establishing whether the user is permitted to gain access at 213 /// This function performs the task of establishing whether the user is permitted to gain access at
194 /// this time. It should be understood that the user has previously been validated by an 214 /// this time. It should be understood that the user has previously been validated by an
195 /// authentication module. This function checks for other things. Such things might be: the time of 215 /// authentication module. This function checks for other things. Such things might be: the time of
196 /// day or the date, the terminal line, remote hostname, etc. This function may also determine 216 /// day or the date, the terminal line, remote hostname, etc. This function may also determine
197 /// things like the expiration on passwords, and respond that the user change it before continuing. 217 /// things like the expiration on passwords, and respond that the user change it before continuing.
198 fn acct_mgmt(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { 218 fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
199 PamResultCode::PAM_IGNORE 219 PamResultCode::PAM_IGNORE
200 } 220 }
201 221
202 /// This function performs the task of authenticating the user. 222 /// This function performs the task of authenticating the user.
203 fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { 223 fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
204 PamResultCode::PAM_IGNORE 224 PamResultCode::PAM_IGNORE
205 } 225 }
206 226
207 /// This function is used to (re-)set the authentication token of the user. 227 /// This function is used to (re-)set the authentication token of the user.
208 /// 228 ///
209 /// The PAM library calls this function twice in succession. The first time with 229 /// The PAM library calls this function twice in succession. The first time with
210 /// PAM_PRELIM_CHECK and then, if the module does not return PAM_TRY_AGAIN, subsequently with 230 /// `PAM_PRELIM_CHECK` and then, if the module does not return `PAM_TRY_AGAIN`, subsequently with
211 /// PAM_UPDATE_AUTHTOK. It is only on the second call that the authorization token is 231 /// `PAM_UPDATE_AUTHTOK`. It is only on the second call that the authorization token is
212 /// (possibly) changed. 232 /// (possibly) changed.
213 fn sm_chauthtok(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { 233 fn sm_chauthtok(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
214 PamResultCode::PAM_IGNORE 234 PamResultCode::PAM_IGNORE
215 } 235 }
216 236
217 /// This function is called to terminate a session. 237 /// This function is called to terminate a session.
218 fn sm_close_session(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { 238 fn sm_close_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
219 PamResultCode::PAM_IGNORE 239 PamResultCode::PAM_IGNORE
220 } 240 }
221 241
222 /// This function is called to commence a session. 242 /// This function is called to commence a session.
223 fn sm_open_session(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { 243 fn sm_open_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
224 PamResultCode::PAM_IGNORE 244 PamResultCode::PAM_IGNORE
225 } 245 }
226 246
227 /// This function performs the task of altering the credentials of the user with respect to the 247 /// This function performs the task of altering the credentials of the user with respect to the
228 /// corresponding authorization scheme. Generally, an authentication module may have access to more 248 /// corresponding authorization scheme. Generally, an authentication module may have access to more
229 /// information about a user than their authentication token. This function is used to make such 249 /// information about a user than their authentication token. This function is used to make such
230 /// information available to the application. It should only be called after the user has been 250 /// information available to the application. It should only be called after the user has been
231 /// authenticated but before a session has been established. 251 /// authenticated but before a session has been established.
232 fn sm_setcred(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode { 252 fn sm_setcred(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
233 PamResultCode::PAM_IGNORE 253 PamResultCode::PAM_IGNORE
234 } 254 }
235 } 255 }