1
|
1 //! Functions for use in pam modules. |
|
2 |
|
3 use libc::{c_char}; |
|
4 use std::{mem, ptr}; |
|
5 use std::ffi::{CStr, CString}; |
|
6 |
|
7 use constants; |
|
8 use constants::*; |
|
9 |
|
10 /// Opaque type, used as a pointer when making pam API calls. |
|
11 /// |
|
12 /// A module is invoked via an external function such as `pam_sm_authenticate`. |
|
13 /// Such a call provides a pam handle pointer. The same pointer should be given |
|
14 /// as an argument when making API calls. |
|
15 #[allow(missing_copy_implementations)] |
|
16 pub enum PamHandleT {} |
|
17 |
|
18 #[allow(missing_copy_implementations)] |
|
19 enum PamItemT {} |
|
20 |
|
21 #[allow(missing_copy_implementations)] |
|
22 pub enum PamDataT {} |
|
23 |
|
24 #[link(name = "pam")] |
|
25 extern { |
|
26 fn pam_get_data(pamh: *const PamHandleT, |
|
27 module_data_name: *const c_char, |
|
28 data: & *mut PamDataT, |
|
29 ) -> PamResultCode; |
|
30 |
|
31 fn pam_set_data(pamh: *const PamHandleT, |
|
32 module_data_name: *const c_char, |
|
33 data: Box<PamDataT>, |
|
34 cleanup: extern fn (pamh: *const PamHandleT, |
|
35 data: Box<PamDataT>, |
|
36 error_status: PamResultCode |
|
37 ), |
|
38 ) -> PamResultCode; |
|
39 |
|
40 fn pam_get_item(pamh: *const PamHandleT, |
|
41 item_type: PamItemType, |
|
42 item: & *mut PamItemT, |
|
43 ) -> PamResultCode; |
|
44 |
|
45 fn pam_set_item(pamh: *mut PamHandleT, |
|
46 item_type: PamItemType, |
|
47 item: &PamItemT, |
|
48 ) -> PamResultCode; |
|
49 |
|
50 fn pam_get_user(pamh: *const PamHandleT, |
|
51 user: & *mut c_char, |
|
52 prompt: *const c_char, |
|
53 ) -> PamResultCode; |
|
54 } |
|
55 |
|
56 pub type PamResult<T> = Result<T, PamResultCode>; |
|
57 |
|
58 /// Type-level mapping for safely retrieving values with `get_item`. |
|
59 /// |
|
60 /// See `pam_get_item` in |
|
61 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html |
|
62 pub trait PamItem { |
|
63 /// Maps a Rust type to a pam constant. |
|
64 /// |
|
65 /// For example, the type PamConv maps to the constant PAM_CONV. The pam |
|
66 /// API contract specifies that when the API function `pam_get_item` is |
|
67 /// called with the constant PAM_CONV, it will return a value of type |
|
68 /// `PamConv`. |
|
69 /// |
|
70 /// The argument will always be `None`. Its purpose is to provide a type |
|
71 /// label - the value is not important. |
|
72 fn item_type(_: Option<Self>) -> PamItemType; |
|
73 } |
|
74 |
|
75 /// Gets some value, identified by `key`, that has been set by the module |
|
76 /// previously. |
|
77 /// |
|
78 /// See `pam_get_data` in |
|
79 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html |
|
80 pub unsafe fn get_data<'a, T>(pamh: &'a PamHandleT, key: &str) -> PamResult<&'a T> { |
|
81 let c_key = CString::new(key).unwrap().as_ptr(); |
|
82 let mut ptr: *mut PamDataT = ptr::null_mut(); |
|
83 let res = pam_get_data(pamh, c_key, &mut ptr); |
|
84 if constants::PAM_SUCCESS == res && !ptr.is_null() { |
|
85 let raw_data: &PamDataT = ptr.as_ref().unwrap(); |
|
86 let data: &T = mem::transmute(raw_data); |
|
87 Ok(data) |
|
88 } |
|
89 else { |
|
90 Err(res) |
|
91 } |
|
92 } |
|
93 |
|
94 /// Stores a value that can be retrieved later with `get_data`. The value lives |
|
95 /// as long as the current pam cycle. |
|
96 /// |
|
97 /// See `pam_set_data` in |
|
98 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html |
|
99 pub fn set_data<T>(pamh: &PamHandleT, key: &str, data: Box<T>) -> PamResult<()> { |
|
100 let c_key = CString::new(key).unwrap().as_ptr(); |
|
101 let res = unsafe { |
|
102 let c_data: Box<PamDataT> = mem::transmute(data); |
|
103 pam_set_data(pamh, c_key, c_data, cleanup::<T>) |
|
104 }; |
|
105 if constants::PAM_SUCCESS == res { Ok(()) } else { Err(res) } |
|
106 } |
|
107 |
|
108 #[no_mangle] |
|
109 pub extern fn cleanup<T>(_: *const PamHandleT, c_data: Box<PamDataT>, _: PamResultCode) { |
|
110 unsafe { |
|
111 let data: Box<T> = mem::transmute(c_data); |
|
112 mem::drop(data); |
|
113 } |
|
114 } |
|
115 |
|
116 /// Retrieves a value that has been set, possibly by the pam client. This is |
|
117 /// particularly useful for getting a `PamConv` reference. |
|
118 /// |
|
119 /// See `pam_get_item` in |
|
120 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html |
|
121 pub fn get_item<'a, T: PamItem>(pamh: &'a PamHandleT) -> PamResult<&'a T> { |
|
122 let ptr: *mut PamItemT = ptr::null_mut(); |
|
123 let (res, item) = unsafe { |
|
124 let r = pam_get_item(pamh, PamItem::item_type(None::<T>), &ptr); |
|
125 let raw_item: &PamItemT = ptr.as_ref().unwrap(); |
|
126 let t: &T = mem::transmute(raw_item); |
|
127 (r, t) |
|
128 }; |
|
129 if constants::PAM_SUCCESS == res { Ok(item) } else { Err(res) } |
|
130 } |
|
131 |
|
132 /// Retrieves the name of the user who is authenticating or logging in. |
|
133 /// |
|
134 /// This is really a specialization of `get_item`. |
|
135 /// |
|
136 /// See `pam_get_user` in |
|
137 /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html |
|
138 pub fn get_user<'a>(pamh: &'a PamHandleT, prompt: Option<&str>) -> PamResult<String> { |
|
139 let ptr: *mut c_char = ptr::null_mut(); |
|
140 let c_prompt = match prompt { |
|
141 Some(p) => CString::new(p).unwrap().as_ptr(), |
|
142 None => ptr::null(), |
|
143 }; |
|
144 let res = unsafe { pam_get_user(pamh, &ptr, c_prompt) }; |
|
145 if constants::PAM_SUCCESS == res && !ptr.is_null() { |
|
146 let const_ptr = ptr as *const c_char; |
|
147 let bytes = unsafe { CStr::from_ptr(const_ptr).to_bytes() }; |
|
148 String::from_utf8(bytes.to_vec()) |
|
149 .map_err(|_| PAM_CONV_ERR) |
|
150 } |
|
151 else { |
|
152 Err(res) |
|
153 } |
|
154 } |