pamsm/pam.rs
1// Copyright (c) 2017 raphael.catolino@gmail.com
2
3#![allow(non_camel_case_types)]
4#![allow(clippy::upper_case_acronyms)]
5
6use pam_types::PamHandle;
7use std::fmt;
8use std::os::raw::c_int;
9
10/// Opaque PAM handle, with additional native methods available via `PamLibExt`.
11#[repr(transparent)]
12pub struct Pam(pub(crate) PamHandle);
13
14impl Pam {
15 /// This allows sending the `Pam` handle to another thread.
16 /// ```rust
17 /// # use pamsm::Pam;
18 /// # fn wrapper(pamh: &mut Pam) {
19 /// std::thread::scope(|s| {
20 /// let borrowed = pamh.as_send_ref();
21 /// s.spawn(move || {
22 /// let pamh: &Pam = borrowed.into();
23 /// });
24 /// });
25 /// # }
26 /// ```
27 /// Synchronized across multiple threads:
28 /// ```rust
29 /// # use pamsm::Pam;
30 /// # fn wrapper(pamh: &mut Pam) {
31 /// std::thread::scope(|s| {
32 /// let shared_1 = std::sync::Arc::new(std::sync::Mutex::new(pamh.as_send_ref()));
33 /// let shared_2 = shared_1.clone();
34 /// s.spawn(move || {
35 /// let pamh: &Pam = &*shared_1.lock().unwrap();
36 /// });
37 /// s.spawn(move || {
38 /// let pamh: &Pam = &*shared_2.lock().unwrap();
39 /// });
40 /// });
41 /// # }
42 /// ```
43 pub fn as_send_ref(&mut self) -> PamSendRef<'_> {
44 PamSendRef(self)
45 }
46}
47
48impl<'a> From<&'a mut Pam> for PamSendRef<'a> {
49 fn from(value: &'a mut Pam) -> Self {
50 Self(value)
51 }
52}
53
54/// This sendable reference to [`Pam`] can be created via [`Pam::as_send_ref`] or `From`/`Into`.
55pub struct PamSendRef<'a>(&'a mut Pam);
56
57unsafe impl<'a> Send for PamSendRef<'a> {}
58
59impl std::ops::Deref for PamSendRef<'_> {
60 type Target = Pam;
61
62 fn deref(&self) -> &Self::Target {
63 self.0
64 }
65}
66
67impl<'a> From<PamSendRef<'a>> for &'a mut Pam {
68 fn from(value: PamSendRef<'a>) -> Self {
69 value.0
70 }
71}
72
73impl<'a> From<PamSendRef<'a>> for &'a Pam {
74 fn from(value: PamSendRef<'a>) -> Self {
75 value.0
76 }
77}
78
79bitflags! {
80 pub struct PamFlags : c_int {
81 const DATA_REPLACE = 0x2000_0000;
82 const SILENT = 0x8000;
83 const DISALLOW_NULL_AUTHTOK = 0x0001;
84 const ESTABLISH_CRED = 0x0002;
85 const DELETE_CRED = 0x0004;
86 const REINITIALIZE_CRED = 0x0008;
87 const REFRESH_CRED = 0x0010;
88 const CHANGE_EXPIRED_AUTHTOK = 0x0020;
89 }
90}
91
92impl fmt::Display for PamError {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94 write!(f, "{:?}", self)
95 }
96}
97
98macro_rules! int_enum {
99 ( $name:ident ($ukey:ident = $uvalue:expr) {
100 $( $key:ident = $value:expr ),*
101 }) => {
102 #[derive(Clone, Copy, Debug, PartialEq)]
103 pub enum $name {
104 $( $key = $value, )*
105 $ukey = $uvalue,
106 }
107 impl $name {
108 #[cfg(feature = "libpam")]
109 pub(crate) fn new(r: c_int) -> $name {
110 match r {
111 $( $value => $name::$key, )*
112 _ => $name::$ukey,
113 }
114 }
115 }
116 }
117}
118
119int_enum! {
120 PamError (UNKNOWN_RESULT = -1) {
121 SUCCESS = 0, /* Successful function return */
122 OPEN_ERR = 1, /* dlopen() failure when dynamically */
123 SYMBOL_ERR = 2, /* Symbol not found */
124 SERVICE_ERR = 3, /* Error in service module */
125 SYSTEM_ERR = 4, /* System error */
126 BUF_ERR = 5, /* Memory buffer error */
127 PERM_DENIED = 6, /* Permission denied */
128 AUTH_ERR = 7, /* Authentication failure */
129 CRED_INSUFFICIENT = 8, /* Can not access authentication data */
130 AUTHINFO_UNAVAIL = 9, /* Underlying authentication service can not retrieve authentication information */
131 USER_UNKNOWN = 10, /* User not known to the underlying authenticaiton module */
132 MAXTRIES = 11, /* An authentication service has maintained a retry count which has been reached. No further retries should be attempted */
133 NEW_AUTHTOK_REQD = 12, /* New authentication token required. */
134 ACCT_EXPIRED = 13, /* User account has expired */
135 SESSION_ERR = 14, /* Can not make/remove an entry for the specified session */
136 CRED_UNAVAIL = 15, /* Underlying authentication service can not retrieve user credentials */
137 CRED_EXPIRED = 16, /* User credentials expired */
138 CRED_ERR = 17, /* Failure setting user credentials */
139 NO_MODULE_DATA = 18, /* No module specific data is present */
140 CONV_ERR = 19, /* Conversation error */
141 AUTHTOK_ERR = 20, /* Authentication token manipulation error */
142 AUTHTOK_RECOVERY_ERR = 21, /* Authentication information cannot be recovered */
143 AUTHTOK_LOCK_BUSY = 22, /* Authentication token lock busy */
144 AUTHTOK_DISABLE_AGING = 23, /* Authentication token aging disabled */
145 TRY_AGAIN = 24, /* Preliminary check by password service */
146 IGNORE = 25, /* Ignore underlying account module regardless of whether the control flag is required, optional, or sufficient */
147 ABORT = 26, /* Critical error (?module fail now request) */
148 AUTHTOK_EXPIRED = 27, /* user's authentication token has expired */
149 MODULE_UNKNOWN = 28, /* module is not known */
150 BAD_ITEM = 29, /* Bad item passed to *_item() */
151 CONV_AGAIN = 30, /* conversation function is event driven and data is not available yet */
152 INCOMPLETE = 31 /* please call this function again to complete authentication stack. Before calling again, verify that conversation is completed */
153 }
154}
155
156/// Default service module implementation.
157/// All default functions return SERVICE_ERR.
158/// You can override functions depending on what kind of module you implement.
159/// See the respective pam_sm_* man pages for documentation.
160pub trait PamServiceModule {
161 fn open_session(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
162 PamError::SERVICE_ERR
163 }
164
165 fn close_session(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
166 PamError::SERVICE_ERR
167 }
168
169 fn authenticate(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
170 PamError::SERVICE_ERR
171 }
172
173 fn setcred(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
174 PamError::SERVICE_ERR
175 }
176
177 fn acct_mgmt(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
178 PamError::SERVICE_ERR
179 }
180
181 fn chauthtok(_: Pam, _: PamFlags, _: Vec<String>) -> PamError {
182 PamError::SERVICE_ERR
183 }
184}
185
186/// Define entrypoints for the PAM module.
187///
188/// This macro must be called exactly once in a PAM module.
189/// It then exports all the pam_sm_* symbols.
190///
191/// The argument to the macro is a type implementing the
192/// `PamServiceModule` trait.
193///
194/// # Example
195///
196/// ```ignore
197/// // lib.rs
198/// #[macro_use] extern crate pamsm;
199///
200/// pam_module!(MyPamService);
201/// ```
202#[macro_export]
203macro_rules! pam_module {
204 ($pamsm_ty:ty) => {
205 // Check trait bound on input type.
206 fn _check_pamsm_trait<T: pamsm::PamServiceModule>() {}
207 fn _t() {
208 _check_pamsm_trait::<$pamsm_ty>()
209 }
210
211 // Callback entry definition.
212 macro_rules! pam_callback {
213 ($pam_cb:ident, $rust_cb:ident) => {
214 #[no_mangle]
215 #[doc(hidden)]
216 pub unsafe extern "C" fn $pam_cb(
217 pamh: pamsm::Pam,
218 flags: std::os::raw::c_int,
219 argc: std::os::raw::c_int,
220 argv: *const *const std::os::raw::c_char,
221 ) -> std::os::raw::c_int {
222 use std::os::raw::c_int;
223 if argc < 0 {
224 return pamsm::PamError::SERVICE_ERR as std::os::raw::c_int;
225 }
226
227 let mut args = Vec::<String>::with_capacity(argc as usize);
228 for count in 0..(argc as isize) {
229 match {
230 std::ffi::CStr::from_ptr(
231 *argv.offset(count) as *const std::os::raw::c_char
232 )
233 .to_str()
234 } {
235 Ok(s) => args.push(s.to_owned()),
236 Err(_) => return pamsm::PamError::SERVICE_ERR as c_int,
237 };
238 }
239 <$pamsm_ty>::$rust_cb(pamh, pamsm::PamFlags::from_bits_unchecked(flags), args) as c_int
240 }
241 };
242 }
243
244 pam_callback!(pam_sm_open_session, open_session);
245 pam_callback!(pam_sm_close_session, close_session);
246 pam_callback!(pam_sm_authenticate, authenticate);
247 pam_callback!(pam_sm_setcred, setcred);
248 pam_callback!(pam_sm_acct_mgmt, acct_mgmt);
249 pam_callback!(pam_sm_chauthtok, chauthtok);
250 };
251}