changeset 20:734ca62159fb

Refactor exported endpoings into pam_hooks macro
author Anthony Nowell <anthony@algorithmia.com>
date Tue, 26 Sep 2017 01:51:39 -0600
parents d654aa0655e5
children aa7e8bd083ef
files pam-http/src/ffi.rs pam-http/src/lib.rs pam-sober/src/ffi.rs pam-sober/src/lib.rs pam/src/hooks.rs pam/src/lib.rs
diffstat 6 files changed, 251 insertions(+), 266 deletions(-) [+]
line wrap: on
line diff
--- a/pam-http/src/ffi.rs	Mon Sep 25 23:42:35 2017 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-use pam::module::{PamHandleT};
-use pam::constants::{PamFlag, PamResultCode, PAM_SILENT};
-use std::ffi::CStr;
-use std::os::raw::{c_char, c_int};
-
-
-
-fn extract_argv(argc: c_int, argv: *const *const c_char) -> Vec<String> {
-    (0..argc)
-        .map(|o| unsafe {
-            CStr::from_ptr(*argv.offset(o as isize) as *const c_char)
-                .to_string_lossy()
-                .into_owned()
-        })
-        .collect()
-}
-
-#[no_mangle]
-pub extern "C" fn pam_sm_acct_mgmt(
-	pamh: &PamHandleT,
-	flags: PamFlag,
-	argc: c_int,
-	argv: *const *const c_char,
-) -> PamResultCode {
-	let args = extract_argv(argc, argv);
-	let silent = (flags & PAM_SILENT) != 0;
-	super::acct_mgmt(pamh, args, silent)
-}
-
-#[no_mangle]
-pub extern "C" fn pam_sm_authenticate(
-	pamh: &PamHandleT,
-	flags: PamFlag,
-	argc: c_int,
-	argv: *const *const c_char,
-) -> PamResultCode {
-	let args = extract_argv(argc, argv);
-	let silent = (flags & PAM_SILENT) != 0;
-	super::sm_authenticate(pamh, args, silent)
-}
-
-#[no_mangle]
-pub extern "C" fn pam_sm_chauthtok(
-	_: &PamHandleT,
-	_: PamFlag,
-	_: c_int,
-	_: *const *const c_char,
-) -> PamResultCode {
-	PamResultCode::PAM_IGNORE
-}
-
-#[no_mangle]
-pub extern "C" fn pam_sm_close_session(
-	_: &PamHandleT,
-	_: PamFlag,
-	_: c_int,
-	_: *const *const c_char,
-) -> PamResultCode {
-	PamResultCode::PAM_IGNORE
-}
-
-#[no_mangle]
-pub extern "C" fn pam_sm_open_session(
-	_: &PamHandleT,
-	_: PamFlag,
-	_: c_int,
-	_: *const *const c_char,
-) -> PamResultCode {
-	PamResultCode::PAM_IGNORE
-}
-
-#[no_mangle]
-pub extern "C" fn pam_sm_setcred(
-	pamh: &PamHandleT,
-	flags: PamFlag,
-	argc: c_int,
-	argv: *const *const c_char,
-) -> PamResultCode {
-	let args = extract_argv(argc, argv);
-	let silent = (flags & PAM_SILENT) != 0;
-	super::sm_setcred(pamh, args, silent)
-}
--- a/pam-http/src/lib.rs	Mon Sep 25 23:42:35 2017 -0600
+++ b/pam-http/src/lib.rs	Tue Sep 26 01:51:39 2017 -0600
@@ -1,14 +1,15 @@
-extern crate pam;
+#[macro_use] extern crate pam;
 extern crate reqwest;
 
-pub mod ffi;
-
-use pam::module::{PamHandleT, get_item, get_user};
-use pam::constants::{PamResultCode, PAM_PROMPT_ECHO_OFF};
+use pam::module::PamHandle;
+use pam::constants::{PamResultCode, PamFlag, PAM_PROMPT_ECHO_OFF};
 use pam::conv::PamConv;
+use pam::hooks::PamHooks;
 use std::collections::HashMap;
 use std::time::Duration;
 use reqwest::{Client, StatusCode};
+use std::ffi::CStr;
+
 
 macro_rules! pam_try {
     ($e:expr) => (
@@ -28,42 +29,59 @@
     );
 }
 
-// This function performs the task of authenticating the user.
-pub fn sm_authenticate(pamh: &PamHandleT, args: Vec<String>, silent: bool) -> PamResultCode {
-    println!("Let's auth over HTTP");
+struct PamHttp;
+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 {
+        println!("Let's auth over HTTP");
 
-    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().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 user = pam_try!(get_user(&pamh, None));
+        let user = pam_try!(pamh.get_user(None));
 
-    let url: &str = match args.get("url") {
-        Some(url) => url,
-        None => return PamResultCode::PAM_AUTH_ERR,
-    };
-    let ca_file = args.get("ca_file");
+        let url: &str = match args.get("url") {
+            Some(url) => url,
+            None => return PamResultCode::PAM_AUTH_ERR,
+        };
+        // let ca_file = args.get("ca_file");
 
-    let conv = match get_item::<PamConv>(&pamh) {
-        Ok(conv) => conv,
-        Err(err) => {
-            println!("Couldn't get pam_conv");
-            return err;
+        let conv = match pamh.get_item::<PamConv>() {
+            Ok(conv) => conv,
+            Err(err) => {
+                println!("Couldn't get pam_conv");
+                return err;
+            }
+        };
+        let password = pam_try!(conv.send(PAM_PROMPT_ECHO_OFF, "Word, yo: "));
+        println!("Got a password {:?}", password);
+        let status = pam_try!(get_url(url, &user, password.as_ref().map(|p|&**p)), PamResultCode::PAM_AUTH_ERR);
+
+        if !status.is_success() {
+            println!("HTTP Error: {}", status);
+            return PamResultCode::PAM_AUTH_ERR;
         }
-    };
-    let password = pam_try!(conv.send(PAM_PROMPT_ECHO_OFF, "Word, yo: "));
-    println!("Got a password {:?}", password);
-    let status = pam_try!(get_url(url, &user, password.as_ref().map(|p|&**p)), PamResultCode::PAM_AUTH_ERR);
 
-    if !status.is_success() {
-        println!("HTTP Error: {}", status);
-        return PamResultCode::PAM_AUTH_ERR;
+        PamResultCode::PAM_SUCCESS
     }
 
-    PamResultCode::PAM_SUCCESS
+    fn sm_setcred(_pamh: &PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
+        println!("set credentials");
+        PamResultCode::PAM_SUCCESS
+    }
+
+    fn acct_mgmt(_pamh: &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)?
@@ -72,22 +90,4 @@
         .map(|r| r.status())
 }
 
-// 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.
-pub fn sm_setcred(_pamh: &PamHandleT, _args: Vec<String>, _silent: bool) -> PamResultCode {
-    println!("set credentials");
-    PamResultCode::PAM_SUCCESS
-}
 
-// This function performs the task of establishing whether the user is permitted to gain access at
-// this time. It should be understood that the user has previously been validated by an
-// 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.
-pub fn acct_mgmt(_pamh: &PamHandleT, _args: Vec<String>, _silent: bool) -> PamResultCode {
-    println!("account management");
-    PamResultCode::PAM_SUCCESS
-}
--- a/pam-sober/src/ffi.rs	Mon Sep 25 23:42:35 2017 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-use pam::module::{PamHandle};
-use pam::constants::{PamFlag, PamResultCode, PAM_SILENT};
-use std::ffi::CStr;
-use std::os::raw::{c_char, c_int};
-
-
-
-fn extract_argv(argc: c_int, argv: *const *const c_char) -> Vec<String> {
-    (0..argc)
-        .map(|o| unsafe {
-            CStr::from_ptr(*argv.offset(o as isize) as *const c_char)
-                .to_string_lossy()
-                .into_owned()
-        })
-        .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);
-	let silent = (flags & PAM_SILENT) != 0;
-	super::acct_mgmt(pamh, args, silent)
-}
-
-#[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);
-	let silent = (flags & PAM_SILENT) != 0;
-	super::sm_authenticate(pamh, args, silent)
-}
-
-#[no_mangle]
-pub extern "C" fn pam_sm_chauthtok(
-	_: &PamHandle,
-	_: PamFlag,
-	_: c_int,
-	_: *const *const c_char,
-) -> PamResultCode {
-	PamResultCode::PAM_IGNORE
-}
-
-#[no_mangle]
-pub extern "C" fn pam_sm_close_session(
-	_: &PamHandle,
-	_: PamFlag,
-	_: c_int,
-	_: *const *const c_char,
-) -> PamResultCode {
-	PamResultCode::PAM_IGNORE
-}
-
-#[no_mangle]
-pub extern "C" fn pam_sm_open_session(
-	_: &PamHandle,
-	_: PamFlag,
-	_: c_int,
-	_: *const *const c_char,
-) -> PamResultCode {
-	PamResultCode::PAM_IGNORE
-}
-
-#[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);
-	let silent = (flags & PAM_SILENT) != 0;
-	super::sm_setcred(pamh, args, silent)
-}
--- a/pam-sober/src/lib.rs	Mon Sep 25 23:42:35 2017 -0600
+++ b/pam-sober/src/lib.rs	Tue Sep 26 01:51:39 2017 -0600
@@ -1,13 +1,13 @@
-extern crate pam;
+#[macro_use] extern crate pam;
 extern crate rand;
 
-pub mod ffi;
-
 use pam::module::PamHandle;
-use pam::constants::{PamResultCode, PAM_PROMPT_ECHO_OFF};
+use pam::constants::{PamResultCode, PamFlag, PAM_PROMPT_ECHO_ON};
 use pam::conv::PamConv;
+use pam::hooks::PamHooks;
 use rand::Rng;
 use std::str::FromStr;
+use std::ffi::CStr;
 
 macro_rules! pam_try {
     ($e:expr) => (
@@ -27,62 +27,57 @@
     );
 }
 
-// This function performs the task of authenticating the user.
-pub fn sm_authenticate(pamh: &PamHandle, args: Vec<String>, silent: bool) -> PamResultCode {
-    println!("Let's auth over HTTP");
+struct PamSober;
+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 {
+        println!("Let's auth over HTTP");
 
-    /* TODO: use args to change difficulty ;-)
-    let args: HashMap<&str, &str> = args.iter().map(|s| {
-        let mut parts = s.splitn(2, "=");
-        (parts.next().unwrap(), parts.next().unwrap_or(""))
-    }).collect();
-    */
+        /* TODO: use args to change difficulty ;-)
+        let args: HashMap<&str, &str> = args.iter().map(|s| {
+            let mut parts = s.splitn(2, "=");
+            (parts.next().unwrap(), parts.next().unwrap_or(""))
+        }).collect();
+        */
 
-    // TODO: maybe we can change difficulty base on user?
-    // let user = pam_try!(pam.get_user(None));
+        // 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,
-        Err(err) => {
-            println!("Couldn't get pam_conv");
-            return err;
-        }
-    };
+        let conv = match pamh.get_item::<PamConv>() {
+            Ok(conv) => conv,
+            Err(err) => {
+                println!("Couldn't get pam_conv");
+                return err;
+            }
+        };
 
-    let mut rng = rand::thread_rng();
-    let a = rng.gen::<u32>() % 100;
-    let b = rng.gen::<u32>() % 100;
-    let math = format!("{} + {} = ", a, b);
+        let mut rng = rand::thread_rng();
+        let a = rng.gen::<u32>() % 100;
+        let b = rng.gen::<u32>() % 100;
+        let math = format!("{} + {} = ", a, b);
+
+        // This println kinda helps debugging since the test script doesn't echo
+        println!("{}", math);
 
-    // This println kinda helps debugging since the test script doesn't echo
-    println!("{}", math);
+        let password = pam_try!(conv.send(PAM_PROMPT_ECHO_ON, &math));
 
-    let password = pam_try!(conv.send(PAM_PROMPT_ECHO_OFF, &math));
+        if password.and_then(|p| u32::from_str(&p).ok()) == Some(a+b) {
+            return PamResultCode::PAM_SUCCESS;
+        }
 
-    if password.and_then(|p| u32::from_str(&p).ok()) == Some(a+b) {
-        return PamResultCode::PAM_SUCCESS;
+        println!("You failed the PAM sobriety test.");
+        return 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 {
+        println!("set credentials");
+        PamResultCode::PAM_SUCCESS
+    }
 
-// 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.
-pub fn sm_setcred(_pamh: &PamHandle, _args: Vec<String>, _silent: bool) -> PamResultCode {
-    println!("set credentials");
-    PamResultCode::PAM_SUCCESS
-}
-
-// This function performs the task of establishing whether the user is permitted to gain access at
-// this time. It should be understood that the user has previously been validated by an
-// 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.
-pub fn acct_mgmt(_pamh: &PamHandle, _args: Vec<String>, _silent: bool) -> PamResultCode {
-    println!("account management");
-    PamResultCode::PAM_SUCCESS
-}
+    fn acct_mgmt(_pamh: &PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
+        println!("account management");
+        PamResultCode::PAM_SUCCESS
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam/src/hooks.rs	Tue Sep 26 01:51:39 2017 -0600
@@ -0,0 +1,153 @@
+use module::{PamHandle};
+use constants::{PamFlag, PamResultCode};
+use std::ffi::CStr;
+
+/// Provides functions that are invoked by the entrypoints generated by the `pam_hooks!` macro.
+///
+/// All of hooks are ignored by PAM dispatch by default given the default return value of `PAM_IGNORE`.
+/// Override any functions that you want to handle with your module. See `man pam(3)`.
+pub trait PamHooks {
+    /// This function performs the task of establishing whether the user is permitted to gain access at
+    /// this time. It should be understood that the user has previously been validated by an
+    /// 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
+	}
+
+    /// This function performs the task of authenticating the user.
+	fn sm_authenticate(_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: &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 commence a session.
+	fn sm_open_session(_pamh: &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
+	}
+}
+
+/// Macro to generate the `extern "C"` entrypoint bindings needed by PAM
+///
+/// You can call `pam_hooks!(SomeType);` for any type that implements `PamHooks`
+#[macro_export]
+macro_rules! pam_hooks {
+	($ident:ident) => (
+		pub use pam_hooks_scope::*;
+		mod pam_hooks_scope {
+			use $crate::module::PamHandle;
+			use $crate::constants::{PamFlag, PamResultCode};
+			use $crate::hooks::PamHooks;
+			use std::ffi::CStr;
+			use std::os::raw::{c_char, c_int};
+
+			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_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_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: &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: &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: &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)
+			}
+		}
+	)
+}
+
+#[cfg(test)]
+pub mod test {
+	use super::PamHooks;
+
+	struct Foo;
+	impl PamHooks for Foo {}
+
+	pam_hooks!(Foo);
+}
\ No newline at end of file
--- a/pam/src/lib.rs	Mon Sep 25 23:42:35 2017 -0600
+++ b/pam/src/lib.rs	Tue Sep 26 01:51:39 2017 -0600
@@ -30,3 +30,4 @@
 pub mod constants;
 pub mod items;
 pub mod module;
+pub mod hooks;