changeset 34:ec70822cbdef

Overhaul
author Andy Caldwell <andrew.caldwell@metaswitch.com>
date Sun, 24 Apr 2022 03:42:11 +0100
parents ea5f195f035f
children 3b2ff50db010
files Cargo.toml pam-http/Cargo.toml pam-http/Justfile pam-http/src/lib.rs pam-sober/Cargo.toml pam-sober/Justfile pam-sober/src/lib.rs pam-sober/test.c pam/Cargo.toml pam/src/constants.rs pam/src/conv.rs pam/src/items.rs pam/src/lib.rs pam/src/macros.rs pam/src/module.rs
diffstat 15 files changed, 409 insertions(+), 388 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Cargo.toml	Sun Apr 24 03:42:11 2022 +0100
@@ -0,0 +1,2 @@
+[workspace]
+members = ["pam", "pam-sober", "pam-http"]
--- a/pam-http/Cargo.toml	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam-http/Cargo.toml	Sun Apr 24 03:42:11 2022 +0100
@@ -9,4 +9,4 @@
 
 [dependencies]
 pam = { path = "../pam/" }
-reqwest = "0.7"
+reqwest = { version = "0.11.3", features = ["blocking"] }
--- a/pam-http/Justfile	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam-http/Justfile	Sun Apr 24 03:42:11 2022 +0100
@@ -3,10 +3,10 @@
     cargo build
 
 install:
-    @cargo build
+    @cargo build --release
     sudo cp conf/http-auth /etc/pam.d/
-    sudo cp target/debug/libpam_http.so /lib/security/pam_http.so
+    sudo cp ../target/release/libpam_http.so /lib/security/pam_http.so
 
 test:
     @just install
-    gcc -o target/pam_test test.c -lpam -lpam_misc
+    gcc -o ../target/pam_test test.c -lpam -lpam_misc
--- a/pam-http/src/lib.rs	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam-http/src/lib.rs	Sun Apr 24 03:42:11 2022 +0100
@@ -1,46 +1,35 @@
-#[macro_use] extern crate pam;
+extern crate pam;
 extern crate reqwest;
 
+use pam::constants::{PamFlag, PamResultCode, PAM_PROMPT_ECHO_OFF};
+use pam::conv::Conv;
 use pam::module::{PamHandle, PamHooks};
-use pam::constants::{PamResultCode, PamFlag, PAM_PROMPT_ECHO_OFF};
-use pam::conv::PamConv;
+use reqwest::blocking::Client;
+use reqwest::StatusCode;
 use std::collections::HashMap;
-use std::time::Duration;
-use reqwest::{Client, StatusCode};
 use std::ffi::CStr;
-
-
-macro_rules! pam_try {
-    ($e:expr) => (
-        match $e {
-            Ok(v) => v,
-            Err(e) => return e,
-        }
-    );
-    ($e:expr, $err:expr) => (
-        match $e {
-            Ok(v) => v,
-            Err(e) => {
-                println!("Error: {}", e);
-                return $err;
-            }
-        }
-    );
-}
+use std::time::Duration;
+use pam::pam_try;
 
 struct PamHttp;
-pam_hooks!(PamHttp);
+pam::pam_hooks!(PamHttp);
 
 impl PamHooks for PamHttp {
     // This function performs the task of authenticating the user.
-    fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
+    fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
         println!("Let's auth over HTTP");
 
-        let args: Vec<_> = args.iter().map(|s| s.to_string_lossy().to_owned() ).collect();
-        let args: HashMap<&str, &str> = args.iter().map(|s| {
-            let mut parts = s.splitn(2, "=");
-            (parts.next().unwrap(), parts.next().unwrap_or(""))
-        }).collect();
+        let args: Vec<_> = args
+            .iter()
+            .map(|s| s.to_string_lossy())
+            .collect();
+        let args: HashMap<&str, &str> = args
+            .iter()
+            .map(|s| {
+                let mut parts = s.splitn(2, '=');
+                (parts.next().unwrap(), parts.next().unwrap_or(""))
+            })
+            .collect();
 
         let user = pam_try!(pamh.get_user(None));
 
@@ -48,18 +37,27 @@
             Some(url) => url,
             None => return PamResultCode::PAM_AUTH_ERR,
         };
-        // let ca_file = args.get("ca_file");
 
-        let conv = match pamh.get_item::<PamConv>() {
-            Ok(conv) => conv,
+        let conv = match pamh.get_item::<Conv>() {
+            Ok(Some(conv)) => conv,
+            Ok(None) => {
+                unreachable!("No conv available");
+            }
             Err(err) => {
                 println!("Couldn't get pam_conv");
                 return err;
             }
         };
         let password = pam_try!(conv.send(PAM_PROMPT_ECHO_OFF, "Word, yo: "));
+        let password = match password {
+            Some(password) => Some(pam_try!(password.to_str(), PamResultCode::PAM_AUTH_ERR)),
+            None => None,
+        };
         println!("Got a password {:?}", password);
-        let status = pam_try!(get_url(url, &user, password.as_ref().map(|p|&**p)), PamResultCode::PAM_AUTH_ERR);
+        let status = pam_try!(
+            get_url(url, &user, password),
+            PamResultCode::PAM_AUTH_ERR
+        );
 
         if !status.is_success() {
             println!("HTTP Error: {}", status);
@@ -69,24 +67,22 @@
         PamResultCode::PAM_SUCCESS
     }
 
-    fn sm_setcred(_pamh: &PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
+    fn sm_setcred(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
         println!("set credentials");
         PamResultCode::PAM_SUCCESS
     }
 
-    fn acct_mgmt(_pamh: &PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
+    fn acct_mgmt(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
         println!("account management");
         PamResultCode::PAM_SUCCESS
     }
 }
 
-
 fn get_url(url: &str, user: &str, password: Option<&str>) -> reqwest::Result<StatusCode> {
-    let client = Client::builder()?.timeout(Duration::from_secs(15)).build()?;
-    client.get(url)?
+    let client = Client::builder().timeout(Duration::from_secs(15)).build()?;
+    client
+        .get(url)
         .basic_auth(user, password)
         .send()
         .map(|r| r.status())
 }
-
-
--- a/pam-sober/Cargo.toml	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam-sober/Cargo.toml	Sun Apr 24 03:42:11 2022 +0100
@@ -9,4 +9,4 @@
 
 [dependencies]
 pam = { path = "../pam/" }
-rand = "0.3.16"
+rand = "0.8.4"
--- a/pam-sober/Justfile	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam-sober/Justfile	Sun Apr 24 03:42:11 2022 +0100
@@ -3,10 +3,10 @@
     cargo build
 
 install:
-    @cargo build
+    @cargo build --release
     sudo cp conf/sober-auth /etc/pam.d/
-    sudo cp target/debug/libpam_sober.so /lib/security/pam_sober.so
+    sudo cp ../target/release/libpam_sober.so /lib/security/pam_sober.so
 
 test:
     @just install
-    gcc -o target/pam_test test.c -lpam -lpam_misc
+    gcc -o ../target/pam_test test.c -lpam -lpam_misc
--- a/pam-sober/src/lib.rs	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam-sober/src/lib.rs	Sun Apr 24 03:42:11 2022 +0100
@@ -1,37 +1,20 @@
-#[macro_use] extern crate pam;
+extern crate pam;
 extern crate rand;
 
+use pam::constants::{PamFlag, PamResultCode, PAM_PROMPT_ECHO_ON};
+use pam::conv::Conv;
 use pam::module::{PamHandle, PamHooks};
-use pam::constants::{PamResultCode, PamFlag, PAM_PROMPT_ECHO_ON};
-use pam::conv::PamConv;
 use rand::Rng;
-use std::str::FromStr;
 use std::ffi::CStr;
-
-macro_rules! pam_try {
-    ($e:expr) => (
-        match $e {
-            Ok(v) => v,
-            Err(e) => return e,
-        }
-    );
-    ($e:expr, $err:expr) => (
-        match $e {
-            Ok(v) => v,
-            Err(e) => {
-                println!("Error: {}", e);
-                return $err;
-            }
-        }
-    );
-}
+use std::str::FromStr;
+use pam::pam_try;
 
 struct PamSober;
-pam_hooks!(PamSober);
+pam::pam_hooks!(PamSober);
 
 impl PamHooks for PamSober {
     // This function performs the task of authenticating the user.
-    fn sm_authenticate(pamh: &PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
+    fn sm_authenticate(pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
         println!("Let's make sure you're sober enough to perform basic addition");
 
         /* TODO: use args to change difficulty ;-)
@@ -44,8 +27,9 @@
         // TODO: maybe we can change difficulty base on user?
         // let user = pam_try!(pam.get_user(None));
 
-        let conv = match pamh.get_item::<PamConv>() {
-            Ok(conv) => conv,
+        let conv = match pamh.get_item::<Conv>() {
+            Ok(Some(conv)) => conv,
+            Ok(None) => todo!(),
             Err(err) => {
                 println!("Couldn't get pam_conv");
                 return err;
@@ -58,24 +42,31 @@
         let math = format!("{} + {} = ", a, b);
 
         // This println kinda helps debugging since the test script doesn't echo
-        println!("{}", math);
+        eprintln!("[DEBUG]: {}{}", math, a + b);
 
         let password = pam_try!(conv.send(PAM_PROMPT_ECHO_ON, &math));
 
-        if password.and_then(|p| u32::from_str(&p).ok()) == Some(a+b) {
-            return PamResultCode::PAM_SUCCESS;
+        if let Some(password) = password {
+            let password = pam_try!(password.to_str(), PamResultCode::PAM_AUTH_ERR);
+            let answer = pam_try!(u32::from_str(password), PamResultCode::PAM_AUTH_ERR);
+            if answer == a + b {
+                PamResultCode::PAM_SUCCESS
+            } else {
+                println!("Wrong answer provided {} + {} != {}", a, b, answer);
+                PamResultCode::PAM_AUTH_ERR
+            }
+        } else {
+            println!("You failed the PAM sobriety test.");
+            PamResultCode::PAM_AUTH_ERR
         }
-
-        println!("You failed the PAM sobriety test.");
-        return PamResultCode::PAM_AUTH_ERR;
     }
 
-    fn sm_setcred(_pamh: &PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
+    fn sm_setcred(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
         println!("set credentials");
         PamResultCode::PAM_SUCCESS
     }
 
-    fn acct_mgmt(_pamh: &PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
+    fn acct_mgmt(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
         println!("account management");
         PamResultCode::PAM_SUCCESS
     }
--- a/pam-sober/test.c	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam-sober/test.c	Sun Apr 24 03:42:11 2022 +0100
@@ -23,18 +23,19 @@
 
 	// Are the credentials correct?
 	if (retval == PAM_SUCCESS) {
-		printf("Credentials accepted.\n");
+		printf("PAM module initialized\n");
 		retval = pam_authenticate(pamh, 0);
 	}
 
 	// Can the accound be used at this time?
 	if (retval == PAM_SUCCESS) {
-		printf("Account is valid.\n");
+		printf("Credentials accepted.\n");
 		retval = pam_acct_mgmt(pamh, 0);
 	}
 
 	// Did everything work?
 	if (retval == PAM_SUCCESS) {
+		printf("Account is valid.\n");
 		printf("Authenticated\n");
 	} else {
 		printf("Not Authenticated\n");
--- a/pam/Cargo.toml	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam/Cargo.toml	Sun Apr 24 03:42:11 2022 +0100
@@ -13,4 +13,4 @@
 name = "pam"
 
 [dependencies]
-libc = "~0.1.5"
+libc = "0.2.97"
--- a/pam/src/constants.rs	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam/src/constants.rs	Sun Apr 24 03:42:11 2022 +0100
@@ -5,7 +5,6 @@
 pub type PamFlag = c_uint;
 pub type PamItemType = c_int;
 pub type PamMessageStyle = c_int;
-pub type AlwaysZero = c_int;
 
 // The Linux-PAM flags
 // see /usr/include/security/_pam_types.h
@@ -17,36 +16,6 @@
 pub const PAM_REFRESH_CRED: PamFlag = 0x0010;
 pub const PAM_CHANGE_EXPIRED_AUTHTOK: PamFlag = 0x0020;
 
-// The Linux-PAM item types
-// see /usr/include/security/_pam_types.h
-/// The service name
-pub const PAM_SERVICE: PamItemType = 1;
-/// The user name
-pub const PAM_USER: PamItemType = 2;
-/// The tty name
-pub const PAM_TTY: PamItemType = 3;
-/// The remote host name
-pub const PAM_RHOST: PamItemType = 4;
-/// The pam_conv structure
-pub const PAM_CONV: PamItemType = 5;
-/// The authentication token (password)
-pub const PAM_AUTHTOK: PamItemType = 6;
-/// The old authentication token
-pub const PAM_OLDAUTHTOK: PamItemType = 7;
-/// The remote user name
-pub const PAM_RUSER: PamItemType = 8;
-/// the prompt for getting a username
-pub const PAM_USER_PROMPT: PamItemType = 9;
-/* Linux-PAM :extensionsPamItemType = */
-/// app supplied function to override failure delays
-pub const PAM_FAIL_DELAY: PamItemType = 10;
-/// X :display name
-pub const PAM_XDISPLAY: PamItemType = 11;
-/// X :server authentication data
-pub const PAM_XAUTHDATA: PamItemType = 12;
-/// The type for pam_get_authtok
-pub const PAM_AUTHTOK_TYPE: PamItemType = 13;
-
 // Message styles
 pub const PAM_PROMPT_ECHO_OFF: PamMessageStyle = 1;
 pub const PAM_PROMPT_ECHO_ON: PamMessageStyle = 2;
--- a/pam/src/conv.rs	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam/src/conv.rs	Sun Apr 24 03:42:11 2022 +0100
@@ -3,11 +3,9 @@
 use std::ptr;
 
 use constants::PamResultCode;
-use constants::*;
-use module::{PamItem, PamResult};
-
-#[allow(missing_copy_implementations)]
-pub enum AppDataPtr {}
+use constants::PamMessageStyle;
+use items::Item;
+use module::PamResult;
 
 #[repr(C)]
 struct PamMessage {
@@ -18,7 +16,7 @@
 #[repr(C)]
 struct PamResponse {
     resp: *const c_char,
-    resp_retcode: AlwaysZero,
+    resp_retcode: libc::c_int, // Unused - always zero
 }
 
 /// `PamConv` acts as a channel for communicating with user.
@@ -27,17 +25,19 @@
 /// pam).  Messages sent will be relayed to the user by the client, and response
 /// will be relayed back.
 #[repr(C)]
-pub struct PamConv {
+pub struct Inner {
     conv: extern "C" fn(
         num_msg: c_int,
         pam_message: &&PamMessage,
         pam_response: &mut *const PamResponse,
-        appdata_ptr: *const AppDataPtr,
+        appdata_ptr: *const libc::c_void,
     ) -> PamResultCode,
-    appdata_ptr: *const AppDataPtr,
+    appdata_ptr: *const libc::c_void,
 }
 
-impl PamConv {
+pub struct Conv<'a>(&'a Inner);
+
+impl<'a> Conv<'a> {
     /// Sends a message to the pam client.
     ///
     /// This will typically result in the user seeing a message or a prompt.
@@ -53,7 +53,7 @@
     /// Note that the user experience will depend on how the client implements
     /// these message styles - and not all applications implement all message
     /// styles.
-    pub fn send(&self, style: PamMessageStyle, msg: &str) -> PamResult<Option<String>> {
+    pub fn send(&self, style: PamMessageStyle, msg: &str) -> PamResult<Option<&CStr>> {
         let mut resp_ptr: *const PamResponse = ptr::null();
         let msg_cstr = CString::new(msg).unwrap();
         let msg = PamMessage {
@@ -61,7 +61,7 @@
             msg: msg_cstr.as_ptr(),
         };
 
-        let ret = (self.conv)(1, &&msg, &mut resp_ptr, self.appdata_ptr);
+        let ret = (self.0.conv)(1, &&msg, &mut resp_ptr, self.0.appdata_ptr);
 
         if PamResultCode::PAM_SUCCESS == ret {
             // PamResponse.resp is null for styles that don't return user input like PAM_TEXT_INFO
@@ -69,8 +69,7 @@
             if response.is_null() {
                 Ok(None)
             } else {
-                let bytes = unsafe { CStr::from_ptr(response).to_bytes() };
-                Ok(String::from_utf8(bytes.to_vec()).ok())
+                Ok(Some(unsafe { CStr::from_ptr(response) }))
             }
         } else {
             Err(ret)
@@ -78,8 +77,18 @@
     }
 }
 
-impl PamItem for PamConv {
-    fn item_type() -> PamItemType {
-        PAM_CONV
+impl<'a> Item for Conv<'a> {
+    type Raw = Inner;
+
+    fn type_id() -> crate::items::ItemType {
+        crate::items::ItemType::Conv
+    }
+
+    unsafe fn from_raw(raw: *const Self::Raw) -> Self {
+        Self(&*raw)
+    }
+
+    fn into_raw(self) -> *const Self::Raw {
+        self.0 as _
     }
 }
--- a/pam/src/items.rs	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam/src/items.rs	Sun Apr 24 03:42:11 2022 +0100
@@ -1,69 +1,88 @@
-use constants::{PamItemType, PAM_SERVICE, PAM_USER, PAM_USER_PROMPT, PAM_TTY, PAM_RUSER, PAM_RHOST,
-                PAM_AUTHTOK, PAM_OLDAUTHTOK};
-use module::PamItem;
-pub use conv::PamConv;
-
-
-pub struct PamService {}
-
-impl PamItem for PamService {
-    fn item_type() -> PamItemType {
-        PAM_SERVICE
-    }
-}
-
-pub struct PamUser {}
-
-impl PamItem for PamUser {
-    fn item_type() -> PamItemType {
-        PAM_USER
-    }
-}
-
-pub struct PamUserPrompt {}
-
-impl PamItem for PamUserPrompt {
-    fn item_type() -> PamItemType {
-        PAM_USER_PROMPT
-    }
+#[repr(u32)]
+pub enum ItemType {
+    /// The service name
+    Service = 1,
+    /// The user name
+    User = 2,
+    /// The tty name
+    Tty = 3,
+    /// The remote host name
+    RHost = 4,
+    /// The pam_conv structure
+    Conv = 5,
+    /// The authentication token (password)
+    AuthTok = 6,
+    /// The old authentication token
+    OldAuthTok = 7,
+    /// The remote user name
+    RUser = 8,
+    /// the prompt for getting a username
+    UserPrompt = 9,
+    /// app supplied function to override failure delays
+    FailDelay = 10,
+    /// X :display name
+    XDisplay = 11,
+    /// X :server authentication data
+    XAuthData = 12,
+    /// The type for pam_get_authtok
+    AuthTokType = 13,
 }
 
-pub struct PamTty {}
+// A type that can be requested by `pam::Handle::get_item`.
+pub trait Item {
+    /// The `repr(C)` type that is returned (by pointer) by the underlying `pam_get_item` function.
+    type Raw;
+
+    /// The `ItemType` for this type
+    fn type_id() -> ItemType;
 
-impl PamItem for PamTty {
-    fn item_type() -> PamItemType {
-        PAM_TTY
-    }
-}
+    /// The function to convert from the pointer to the C-representation to this safer wrapper type
+    ///
+    /// # Safety
+    ///
+    /// This function can assume the pointer is a valid pointer to a `Self::Raw` instance.
+    unsafe fn from_raw(raw: *const Self::Raw) -> Self;
 
-pub struct PamRUser {}
-
-impl PamItem for PamRUser {
-    fn item_type() -> PamItemType {
-        PAM_RUSER
-    }
+    /// The function to convert from this wrapper type to a C-compatible pointer.
+    fn into_raw(self) -> *const Self::Raw;
 }
 
-pub struct PamRHost {}
+macro_rules! cstr_item {
+    ($name:ident) => {
+        #[derive(Debug)]
+        pub struct $name<'s>(pub &'s std::ffi::CStr);
+
+        impl<'s> std::ops::Deref for $name<'s> {
+            type Target = &'s std::ffi::CStr;
+            fn deref(&self) -> &Self::Target {
+                &self.0
+            }
+        }
 
-impl PamItem for PamRHost {
-    fn item_type() -> PamItemType {
-        PAM_RHOST
-    }
+        impl<'s> Item for $name<'s> {
+            type Raw = libc::c_char;
+
+            fn type_id() -> ItemType {
+                ItemType::$name
+            }
+
+            unsafe fn from_raw(raw: *const Self::Raw) -> Self {
+                Self(std::ffi::CStr::from_ptr(raw))
+            }
+
+            fn into_raw(self) -> *const Self::Raw {
+                self.0.as_ptr()
+            }
+        }
+    };
 }
 
-pub struct PamAuthTok {}
-
-impl PamItem for PamAuthTok {
-    fn item_type() -> PamItemType {
-        PAM_AUTHTOK
-    }
-}
-
-pub struct PamOldAuthTok {}
-
-impl PamItem for PamOldAuthTok {
-    fn item_type() -> PamItemType {
-        PAM_OLDAUTHTOK
-    }
-}
+cstr_item!(Service);
+cstr_item!(User);
+cstr_item!(Tty);
+cstr_item!(RHost);
+// Conv
+cstr_item!(AuthTok);
+cstr_item!(OldAuthTok);
+cstr_item!(RUser);
+cstr_item!(UserPrompt);
--- a/pam/src/lib.rs	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam/src/lib.rs	Sun Apr 24 03:42:11 2022 +0100
@@ -26,9 +26,9 @@
 
 extern crate libc;
 
+pub mod constants;
+pub mod conv;
+pub mod items;
 #[doc(hidden)]
 pub mod macros;
-pub mod conv;
-pub mod constants;
-pub mod items;
 pub mod module;
--- a/pam/src/macros.rs	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam/src/macros.rs	Sun Apr 24 03:42:11 2022 +0100
@@ -18,12 +18,12 @@
 /// pam_hooks!(MyPamModule);
 ///
 /// impl PamHooks for MyPamModule {
-///	   fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+///    fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
 ///        println!("Everybody is authenticated!");
 ///        PamResultCode::PAM_SUCCESS
 ///    }
 ///
-///    fn acct_mgmt(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+///    fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
 ///        println!("Everybody is authorized!");
 ///        PamResultCode::PAM_SUCCESS
 ///    }
@@ -31,97 +31,111 @@
 /// ```
 #[macro_export]
 macro_rules! pam_hooks {
-	($ident:ident) => (
-		pub use self::pam_hooks_scope::*;
-		mod pam_hooks_scope {
-			use $crate::module::{PamHandle, PamHooks};
-			use $crate::constants::{PamFlag, PamResultCode};
-			use std::ffi::CStr;
-			use std::os::raw::{c_char, c_int};
+    ($ident:ident) => {
+        pub use self::pam_hooks_scope::*;
+        mod pam_hooks_scope {
+            use std::ffi::CStr;
+            use std::os::raw::{c_char, c_int};
+            use $crate::constants::{PamFlag, PamResultCode};
+            use $crate::module::{PamHandle, PamHooks};
 
-			fn extract_argv<'a>(argc: c_int, argv: *const *const c_char) -> Vec<&'a CStr> {
-				(0..argc)
-					.map(|o| unsafe {
-						CStr::from_ptr(*argv.offset(o as isize) as *const c_char)
-					})
-					.collect()
-			}
+            fn extract_argv<'a>(argc: c_int, argv: *const *const c_char) -> Vec<&'a CStr> {
+                (0..argc)
+                    .map(|o| unsafe { CStr::from_ptr(*argv.offset(o as isize) as *const c_char) })
+                    .collect()
+            }
 
-			#[no_mangle]
-			pub extern "C" fn pam_sm_acct_mgmt(
-				pamh: &PamHandle,
-				flags: PamFlag,
-				argc: c_int,
-				argv: *const *const c_char,
-			) -> PamResultCode {
-				let args = extract_argv(argc, argv);
-				super::$ident::acct_mgmt(pamh, args, flags)
-			}
+            #[no_mangle]
+            pub extern "C" fn pam_sm_acct_mgmt(
+                pamh: &mut PamHandle,
+                flags: PamFlag,
+                argc: c_int,
+                argv: *const *const c_char,
+            ) -> PamResultCode {
+                let args = extract_argv(argc, argv);
+                super::$ident::acct_mgmt(pamh, args, flags)
+            }
 
-			#[no_mangle]
-			pub extern "C" fn pam_sm_authenticate(
-				pamh: &PamHandle,
-				flags: PamFlag,
-				argc: c_int,
-				argv: *const *const c_char,
-			) -> PamResultCode {
-				let args = extract_argv(argc, argv);
-				super::$ident::sm_authenticate(pamh, args, flags)
-			}
+            #[no_mangle]
+            pub extern "C" fn pam_sm_authenticate(
+                pamh: &mut PamHandle,
+                flags: PamFlag,
+                argc: c_int,
+                argv: *const *const c_char,
+            ) -> PamResultCode {
+                let args = extract_argv(argc, argv);
+                super::$ident::sm_authenticate(pamh, args, flags)
+            }
+
+            #[no_mangle]
+            pub extern "C" fn pam_sm_chauthtok(
+                pamh: &mut PamHandle,
+                flags: PamFlag,
+                argc: c_int,
+                argv: *const *const c_char,
+            ) -> PamResultCode {
+                let args = extract_argv(argc, argv);
+                super::$ident::sm_chauthtok(pamh, args, flags)
+            }
 
-			#[no_mangle]
-			pub extern "C" fn pam_sm_chauthtok(
-				pamh: &PamHandle,
-				flags: PamFlag,
-				argc: c_int,
-				argv: *const *const c_char,
-			) -> PamResultCode {
-				let args = extract_argv(argc, argv);
-				super::$ident::sm_chauthtok(pamh, args, flags)
-			}
+            #[no_mangle]
+            pub extern "C" fn pam_sm_close_session(
+                pamh: &mut PamHandle,
+                flags: PamFlag,
+                argc: c_int,
+                argv: *const *const c_char,
+            ) -> PamResultCode {
+                let args = extract_argv(argc, argv);
+                super::$ident::sm_close_session(pamh, args, flags)
+            }
 
-			#[no_mangle]
-			pub extern "C" fn pam_sm_close_session(
-				pamh: &PamHandle,
-				flags: PamFlag,
-				argc: c_int,
-				argv: *const *const c_char,
-			) -> PamResultCode {
-				let args = extract_argv(argc, argv);
-				super::$ident::sm_close_session(pamh, args, flags)
-			}
+            #[no_mangle]
+            pub extern "C" fn pam_sm_open_session(
+                pamh: &mut PamHandle,
+                flags: PamFlag,
+                argc: c_int,
+                argv: *const *const c_char,
+            ) -> PamResultCode {
+                let args = extract_argv(argc, argv);
+                super::$ident::sm_open_session(pamh, args, flags)
+            }
 
-			#[no_mangle]
-			pub extern "C" fn pam_sm_open_session(
-				pamh: &PamHandle,
-				flags: PamFlag,
-				argc: c_int,
-				argv: *const *const c_char,
-			) -> PamResultCode {
-				let args = extract_argv(argc, argv);
-				super::$ident::sm_open_session(pamh, args, flags)
-			}
+            #[no_mangle]
+            pub extern "C" fn pam_sm_setcred(
+                pamh: &mut PamHandle,
+                flags: PamFlag,
+                argc: c_int,
+                argv: *const *const c_char,
+            ) -> PamResultCode {
+                let args = extract_argv(argc, argv);
+                super::$ident::sm_setcred(pamh, args, flags)
+            }
+        }
+    };
+}
 
-			#[no_mangle]
-			pub extern "C" fn pam_sm_setcred(
-				pamh: &PamHandle,
-				flags: PamFlag,
-				argc: c_int,
-				argv: *const *const c_char,
-			) -> PamResultCode {
-				let args = extract_argv(argc, argv);
-				super::$ident::sm_setcred(pamh, args, flags)
-			}
-		}
-	)
+#[macro_export]
+macro_rules! pam_try {
+    ($r:expr) => {
+        match $r {
+            Ok(t) => t,
+            Err(e) => return e,
+        }
+    };
+    ($r:expr, $e:expr) => {
+        match $r {
+            Ok(t) => t,
+            Err(_) => return $e,
+        }
+    };
 }
 
 #[cfg(test)]
 pub mod test {
-	use module::PamHooks;
+    use module::PamHooks;
 
-	struct Foo;
-	impl PamHooks for Foo {}
+    struct Foo;
+    impl PamHooks for Foo {}
 
-	pam_hooks!(Foo);
-}
\ No newline at end of file
+    pam_hooks!(Foo);
+}
--- a/pam/src/module.rs	Thu Jul 11 19:13:19 2019 -0700
+++ b/pam/src/module.rs	Sun Apr 24 03:42:11 2022 +0100
@@ -1,93 +1,87 @@
 //! Functions for use in pam modules.
 
 use libc::c_char;
-use std::{mem, ptr};
 use std::ffi::{CStr, CString};
 
-use constants::{PamResultCode, PamItemType, PamFlag};
+use constants::{PamFlag, PamResultCode};
 
 /// Opaque type, used as a pointer when making pam API calls.
 ///
 /// A module is invoked via an external function such as `pam_sm_authenticate`.
 /// Such a call provides a pam handle pointer.  The same pointer should be given
 /// as an argument when making API calls.
-#[allow(missing_copy_implementations)]
-pub enum PamHandle {}
-
-#[allow(missing_copy_implementations)]
-enum PamItemT {}
-
-#[allow(missing_copy_implementations)]
-pub enum PamDataT {}
+#[repr(C)]
+pub struct PamHandle {
+    _data: [u8; 0],
+}
 
 #[link(name = "pam")]
 extern "C" {
-    fn pam_get_data(pamh: *const PamHandle,
-                    module_data_name: *const c_char,
-                    data: &mut *const PamDataT)
-                    -> PamResultCode;
+    fn pam_get_data(
+        pamh: *const PamHandle,
+        module_data_name: *const c_char,
+        data: &mut *const libc::c_void,
+    ) -> PamResultCode;
 
-    fn pam_set_data(pamh: *const PamHandle,
-                    module_data_name: *const c_char,
-                    data: Box<PamDataT>,
-                    cleanup: extern "C" fn(pamh: *const PamHandle,
-                                           data: Box<PamDataT>,
-                                           error_status: PamResultCode))
-                    -> PamResultCode;
+    fn pam_set_data(
+        pamh: *const PamHandle,
+        module_data_name: *const c_char,
+        data: *mut libc::c_void,
+        cleanup: extern "C" fn(
+            pamh: *const PamHandle,
+            data: *mut libc::c_void,
+            error_status: PamResultCode,
+        ),
+    ) -> PamResultCode;
 
-    fn pam_get_item(pamh: *const PamHandle,
-                    item_type: PamItemType,
-                    item: &mut *const PamItemT)
-                    -> PamResultCode;
+    fn pam_get_item(
+        pamh: *const PamHandle,
+        item_type: crate::items::ItemType,
+        item: &mut *const libc::c_void,
+    ) -> PamResultCode;
 
-    fn pam_set_item(pamh: *mut PamHandle,
-                    item_type: PamItemType,
-                    item: &PamItemT)
-                    -> PamResultCode;
+    fn pam_set_item(
+        pamh: *mut PamHandle,
+        item_type: crate::items::ItemType,
+        item: *const libc::c_void,
+    ) -> PamResultCode;
 
-    fn pam_get_user(pamh: *const PamHandle,
-                    user: &*mut c_char,
-                    prompt: *const c_char)
-                    -> PamResultCode;
+    fn pam_get_user(
+        pamh: *const PamHandle,
+        user: &*mut c_char,
+        prompt: *const c_char,
+    ) -> PamResultCode;
 }
 
-#[no_mangle]
-pub extern "C" fn cleanup<T>(_: *const PamHandle, c_data: Box<PamDataT>, _: PamResultCode) {
+pub extern "C" fn cleanup<T>(_: *const PamHandle, c_data: *mut libc::c_void, _: PamResultCode) {
     unsafe {
-        let data: Box<T> = mem::transmute(c_data);
-        mem::drop(data);
+        let _data: Box<T> = Box::from_raw(c_data.cast::<T>());
     }
 }
 
 pub type PamResult<T> = Result<T, PamResultCode>;
 
-/// Type-level mapping for safely retrieving values with `get_item`.
-///
-/// See `pam_get_item` in
-/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
-pub trait PamItem {
-    /// Maps a Rust type to a pam constant.
-    ///
-    /// For example, the type PamConv maps to the constant PAM_CONV.  The pam
-    /// API contract specifies that when the API function `pam_get_item` is
-    /// called with the constant PAM_CONV, it will return a value of type
-    /// `PamConv`.
-    fn item_type() -> PamItemType;
-}
-
-
 impl PamHandle {
     /// Gets some value, identified by `key`, that has been set by the module
     /// previously.
     ///
     /// See `pam_get_data` in
     /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the underlying PAM function call fails.
+    ///
+    /// # Safety
+    ///
+    /// The data stored under the provided key must be of type `T` otherwise the
+    /// behaviour of this funtion is undefined.
     pub unsafe fn get_data<'a, T>(&'a self, key: &str) -> PamResult<&'a T> {
-        let c_key = CString::new(key).unwrap().as_ptr();
-        let mut ptr: *const PamDataT = ptr::null();
-        let res = pam_get_data(self, c_key, &mut ptr);
+        let c_key = CString::new(key).unwrap();
+        let mut ptr: *const libc::c_void = std::ptr::null();
+        let res = pam_get_data(self, c_key.as_ptr(), &mut ptr);
         if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
-            let typed_ptr: *const T = mem::transmute(ptr);
+            let typed_ptr = ptr.cast::<T>();
             let data: &T = &*typed_ptr;
             Ok(data)
         } else {
@@ -100,11 +94,19 @@
     ///
     /// See `pam_set_data` in
     /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the underlying PAM function call fails.
     pub fn set_data<T>(&self, key: &str, data: Box<T>) -> PamResult<()> {
-        let c_key = CString::new(key).unwrap().as_ptr();
+        let c_key = CString::new(key).unwrap();
         let res = unsafe {
-            let c_data: Box<PamDataT> = mem::transmute(data);
-            pam_set_data(self, c_key, c_data, cleanup::<T>)
+            pam_set_data(
+                self,
+                c_key.as_ptr(),
+                Box::into_raw(data).cast::<libc::c_void>(),
+                cleanup::<T>,
+            )
         };
         if PamResultCode::PAM_SUCCESS == res {
             Ok(())
@@ -113,19 +115,25 @@
         }
     }
 
-
-
     /// Retrieves a value that has been set, possibly by the pam client.  This is
     /// particularly useful for getting a `PamConv` reference.
     ///
     /// See `pam_get_item` in
     /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
-    pub fn get_item<'a, T: PamItem>(&self) -> PamResult<&'a T> {
-        let mut ptr: *const PamItemT = ptr::null();
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the underlying PAM function call fails.
+    pub fn get_item<T: crate::items::Item>(&self) -> PamResult<Option<T>> {
+        let mut ptr: *const libc::c_void = std::ptr::null();
         let (res, item) = unsafe {
-            let r = pam_get_item(self, T::item_type(), &mut ptr);
-            let typed_ptr: *const T = mem::transmute(ptr);
-            let t: &T = &*typed_ptr;
+            let r = pam_get_item(self, T::type_id(), &mut ptr);
+            let typed_ptr = ptr.cast::<T::Raw>();
+            let t = if typed_ptr.is_null() {
+                None
+            } else {
+                Some(T::from_raw(typed_ptr))
+            };
             (r, t)
         };
         if PamResultCode::PAM_SUCCESS == res {
@@ -142,17 +150,17 @@
     ///
     /// See `pam_set_item` in
     /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
-    pub fn set_item_str<T: PamItem>(&mut self, item: &str) -> PamResult<()> {
-        let c_item = CString::new(item).unwrap().as_ptr();
-
-        let res = unsafe {
-            pam_set_item(self,
-                        T::item_type(),
-
-                        // unwrapping is okay here, as c_item will not be a NULL
-                        // pointer
-                        (c_item as *const PamItemT).as_ref().unwrap())
-        };
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the underlying PAM function call fails.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the provided item key contains a nul byte
+    pub fn set_item_str<T: crate::items::Item>(&mut self, item: T) -> PamResult<()> {
+        let res =
+            unsafe { pam_set_item(self, T::type_id(), item.into_raw().cast::<libc::c_void>())};
         if PamResultCode::PAM_SUCCESS == res {
             Ok(())
         } else {
@@ -166,11 +174,23 @@
     ///
     /// See `pam_get_user` in
     /// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the underlying PAM function call fails.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the provided prompt string contains a nul byte
     pub fn get_user(&self, prompt: Option<&str>) -> PamResult<String> {
-        let ptr: *mut c_char = ptr::null_mut();
+        let ptr: *mut c_char = std::ptr::null_mut();
+        let prompt_string;
         let c_prompt = match prompt {
-            Some(p) => CString::new(p).unwrap().as_ptr(),
-            None => ptr::null(),
+            Some(p) => {
+                prompt_string = CString::new(p).unwrap();
+                prompt_string.as_ptr()
+            }
+            None => std::ptr::null(),
         };
         let res = unsafe { pam_get_user(self, &ptr, c_prompt) };
         if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
@@ -195,41 +215,41 @@
     /// authentication module. This function checks for other things. Such things might be: the time of
     /// day or the date, the terminal line, remote hostname, etc. This function may also determine
     /// things like the expiration on passwords, and respond that the user change it before continuing.
-	fn acct_mgmt(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-		PamResultCode::PAM_IGNORE
-	}
+    fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+        PamResultCode::PAM_IGNORE
+    }
 
     /// This function performs the task of authenticating the user.
-	fn sm_authenticate(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-		PamResultCode::PAM_IGNORE
-	}
+    fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+        PamResultCode::PAM_IGNORE
+    }
 
-	/// This function is used to (re-)set the authentication token of the user.
-	///
-	/// The PAM library calls this function twice in succession. The first time with
-	/// PAM_PRELIM_CHECK and then, if the module does not return PAM_TRY_AGAIN, subsequently with
-	/// PAM_UPDATE_AUTHTOK. It is only on the second call that the authorization token is
-	/// (possibly) changed.
-	fn sm_chauthtok(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-		PamResultCode::PAM_IGNORE
-	}
+    /// This function is used to (re-)set the authentication token of the user.
+    ///
+    /// The PAM library calls this function twice in succession. The first time with
+    /// `PAM_PRELIM_CHECK` and then, if the module does not return `PAM_TRY_AGAIN`, subsequently with
+    /// `PAM_UPDATE_AUTHTOK`. It is only on the second call that the authorization token is
+    /// (possibly) changed.
+    fn sm_chauthtok(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+        PamResultCode::PAM_IGNORE
+    }
 
-	/// This function is called to terminate a session.
-	fn sm_close_session(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-		PamResultCode::PAM_IGNORE
-	}
+    /// This function is called to terminate a session.
+    fn sm_close_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+        PamResultCode::PAM_IGNORE
+    }
 
-	/// This function is called to commence a session.
-	fn sm_open_session(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-		PamResultCode::PAM_IGNORE
-	}
+    /// This function is called to commence a session.
+    fn sm_open_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+        PamResultCode::PAM_IGNORE
+    }
 
     /// This function performs the task of altering the credentials of the user with respect to the
     /// corresponding authorization scheme. Generally, an authentication module may have access to more
     /// information about a user than their authentication token. This function is used to make such
     /// information available to the application. It should only be called after the user has been
     /// authenticated but before a session has been established.
-	fn sm_setcred(pamh: &PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-		PamResultCode::PAM_IGNORE
-	}
-}
\ No newline at end of file
+    fn sm_setcred(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+        PamResultCode::PAM_IGNORE
+    }
+}