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}