Mercurial > crates > nonstick
changeset 163:a75a66cb4181
Add end-to-end tests; fix issues found by tests.
- Create tests and installer/remover shell script
- Fix Pointer/pointee problems
- Add Debug formatting
- Misc cleanup
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Mon, 14 Jul 2025 17:40:11 -0400 |
parents | 180237d0b498 |
children | d0bba0117456 |
files | libpam-sys/src/lib.rs src/libpam/conversation.rs src/libpam/environ.rs src/libpam/handle.rs src/libpam/items.rs testharness/install-test-harness.sh testharness/nonstick_testharness.conf testharness/src/bin/testharness.rs testharness/src/lib.rs |
diffstat | 9 files changed, 282 insertions(+), 93 deletions(-) [+] |
line wrap: on
line diff
--- a/libpam-sys/src/lib.rs Mon Jul 14 15:07:16 2025 -0400 +++ b/libpam-sys/src/lib.rs Mon Jul 14 17:40:11 2025 -0400 @@ -44,6 +44,7 @@ /// Used by PAM to communicate between the module and the application. #[repr(C)] +#[derive(Debug)] pub struct pam_conv { pub conv: unsafe extern "C" fn( num_msg: c_int, @@ -56,6 +57,7 @@ /// A message sent into a PAM conversation. #[repr(C)] +#[derive(Debug)] pub struct pam_message { pub msg_style: c_int, pub msg: *const c_char, @@ -63,6 +65,7 @@ /// A response returned from a PAM conversation. #[repr(C)] +#[derive(Debug)] pub struct pam_response { pub resp: *mut c_char, /// Completely unused.
--- a/src/libpam/conversation.rs Mon Jul 14 15:07:16 2025 -0400 +++ b/src/libpam/conversation.rs Mon Jul 14 17:40:11 2025 -0400 @@ -69,6 +69,7 @@ } /// A conversation owned by a PAM handle and lent to us. +#[derive(Debug)] pub struct PamConv(libpam_sys::pam_conv); impl Conversation for PamConv {
--- a/src/libpam/environ.rs Mon Jul 14 15:07:16 2025 -0400 +++ b/src/libpam/environ.rs Mon Jul 14 17:40:11 2025 -0400 @@ -11,7 +11,7 @@ fn environ_get(&self, key: &OsStr) -> Option<OsString> { let key = CString::new(key.as_bytes()).ok()?; // SAFETY: We are a valid handle and are calling with a good key. - let src = unsafe { libpam_sys::pam_getenv(self.raw_ref(), key.as_ptr()) }; + let src = unsafe { libpam_sys::pam_getenv(self.inner(), key.as_ptr()) }; let val = match NonNull::new(src) { None => return None, Some(ptr) => ptr.as_ptr(), @@ -39,14 +39,14 @@ let put = CString::new(result).unwrap(); // SAFETY: This is a valid handle and a valid environment string. // pam_putenv is only ever going to - let _ = unsafe { libpam_sys::pam_putenv(self.raw_mut(), put.as_ptr()) }; + let _ = unsafe { libpam_sys::pam_putenv(self.inner_mut(), put.as_ptr()) }; old } fn environ_iter(&self) -> impl Iterator<Item = (OsString, OsString)> { // SAFETY: This is a valid PAM handle. It will return valid data. unsafe { - NonNull::new(libpam_sys::pam_getenvlist(self.raw_ref())) + NonNull::new(libpam_sys::pam_getenvlist(self.inner())) .map(|ptr| EnvList::from_ptr(ptr.cast())) .unwrap_or_else(EnvList::empty) }
--- a/src/libpam/handle.rs Mon Jul 14 15:07:16 2025 -0400 +++ b/src/libpam/handle.rs Mon Jul 14 17:40:11 2025 -0400 @@ -18,12 +18,12 @@ use std::mem::ManuallyDrop; use std::os::unix::ffi::OsStrExt; use std::ptr::NonNull; -use std::{fmt, ptr}; +use std::{any, fmt, ptr}; /// An owned PAM handle. pub struct LibPamTransaction<C: Conversation> { - /// The handle itself. - handle: ManuallyDrop<LibPamHandle>, + /// The handle itself. We guarantee this will not be null. + handle: *mut LibPamHandle, /// The last return value from the handle. last_return: Cell<Result<()>>, /// If set, the Conversation that this PAM handle owns. @@ -36,6 +36,16 @@ conversation: Box<OwnedConversation<C>>, } +impl<C: Conversation> fmt::Debug for LibPamTransaction<C> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct(any::type_name::<Self>()) + .field("handle", &format!("{:p}", self.handle)) + .field("last_return", &self.last_return.get()) + .field("conversation", &format!("{:p}", self.conversation)) + .finish() + } +} + #[derive(Debug, PartialEq)] pub struct TransactionBuilder { service_name: OsString, @@ -76,35 +86,34 @@ } /// Builds the PAM handle and starts the transaction. - pub fn build(self, conv: impl Conversation) -> Result<LibPamTransaction<impl Conversation>> { + pub fn build<C: Conversation>(self, conv: C) -> Result<LibPamTransaction<C>> { LibPamTransaction::start(self.service_name, self.username, conv) } } impl<C: Conversation> LibPamTransaction<C> { fn start(service_name: OsString, username: Option<OsString>, conversation: C) -> Result<Self> { - let conv = Box::new(OwnedConversation::new(conversation)); + let mut conv = Box::new(OwnedConversation::new(conversation)); let service_cstr = CString::new(service_name.as_bytes()).expect("null is forbidden"); let username_cstr = memory::option_cstr_os(username.as_deref()); let username_cstr = memory::prompt_ptr(username_cstr.as_deref()); let mut handle: *mut libpam_sys::pam_handle = ptr::null_mut(); + let conv_ptr: *mut OwnedConversation<_> = conv.as_mut() as _; // SAFETY: We've set everything up properly to call `pam_start`. // The returned value will be a valid pointer provided the result is OK. let result = unsafe { libpam_sys::pam_start( service_cstr.as_ptr(), username_cstr, - (conv.as_ref() as *const OwnedConversation<C>) - .cast_mut() - .cast(), + conv_ptr.cast(), &mut handle, ) }; ErrorCode::result_from(result)?; let handle = NonNull::new(handle).ok_or(ErrorCode::BufferError)?; Ok(Self { - handle: ManuallyDrop::new(LibPamHandle(handle)), + handle: handle.as_ptr().cast(), last_return: Cell::new(Ok(())), conversation: conv, }) @@ -139,14 +148,16 @@ /// Internal "end" function, which binary-ORs the status with `or_with`. fn end_internal(&mut self, or_with: i32) { let result = ErrorCode::result_to_c(self.last_return.get()) | or_with; - unsafe { libpam_sys::pam_end(self.handle.raw_mut(), result) }; + unsafe { libpam_sys::pam_end(self.handle.cast(), result) }; } } macro_rules! wrap { (fn $name:ident { $pam_func:ident }) => { fn $name(&mut self, flags: Flags) -> Result<()> { - ErrorCode::result_from(unsafe { libpam_sys::$pam_func(self.0.as_mut(), flags.bits()) }) + ErrorCode::result_from(unsafe { + libpam_sys::$pam_func((self as *mut Self).cast(), flags.bits()) + }) } }; } @@ -160,7 +171,6 @@ // TODO: pam_setcred - app // pam_open_session - app // pam_close_session - app -// pam_set/get_data - module impl<C: Conversation> Drop for LibPamTransaction<C> { /// Closes the PAM session on an owned PAM handle. @@ -181,14 +191,14 @@ // First have the kind that save the result after delegation. (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => { fn $meth(&self $(, $param: $typ)*) -> Result<$ret> { - let result = self.handle.$meth($($param),*); + let result = unsafe { &*self.handle }.$meth($($param),*); self.last_return.set(split(&result)); result } }; (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> Result<$ret:ty>) => { fn $meth(&mut self $(, $param: $typ)*) -> Result<$ret> { - let result = self.handle.$meth($($param),*); + let result = unsafe { &mut *self.handle }.$meth($($param),*); self.last_return.set(split(&result)); result } @@ -196,12 +206,12 @@ // Then have the kind that are just raw delegates (fn $meth:ident(&self $(, $param:ident: $typ:ty)*) -> $ret:ty) => { fn $meth(&self $(, $param: $typ)*) -> $ret { - self.handle.$meth($($param),*) + unsafe { &*self.handle }.$meth($($param),*) } }; (fn $meth:ident(&mut self $(, $param:ident: $typ:ty)*) -> $ret:ty) => { fn $meth(&mut self $(, $param: $typ)*) -> $ret { - self.handle.$meth($($param),*) + unsafe { &mut *self.handle }.$meth($($param),*) } }; // Then have item getters / setters @@ -245,22 +255,9 @@ /// If [`Self::end`] is not called, this will always call `pam_end` reporting /// successful completion. #[repr(transparent)] -pub struct LibPamHandle(NonNull<libpam_sys::pam_handle>); +pub struct LibPamHandle(libpam_sys::pam_handle); impl LibPamHandle { - /// Takes ownership of the pointer to the given PAM handle. - /// - /// **Do not use this just to get a reference to a PAM handle.** - /// - /// # Safety - /// - /// - The pointer must point to a valid PAM handle. - /// - The conversation associated with the handle must remain valid - /// for as long as the handle is open. - pub unsafe fn from_ptr(handle: NonNull<libpam_sys::pam_handle>) -> Self { - Self(handle) - } - /// Ends the transaction, reporting `error_code` to cleanup callbacks. /// /// # References @@ -268,9 +265,8 @@ /// #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] #[doc = stdlinks!(3 pam_end)] - pub fn end(self, result: Result<()>) { - let mut me = ManuallyDrop::new(self); - unsafe { libpam_sys::pam_end(me.raw_mut(), ErrorCode::result_to_c(result)) }; + pub fn end(&mut self, result: Result<()>) { + unsafe { libpam_sys::pam_end(self.inner_mut(), ErrorCode::result_to_c(result)) }; } #[cfg_attr( @@ -294,35 +290,22 @@ /// #[doc = guide!(adg: "adg-interface-by-app-expected.html#adg-pam_end")] #[doc = stdlinks!(3 pam_end)] - pub fn end_silent(self, result: Result<()>) { - let mut me = ManuallyDrop::new(self); + pub fn end_silent(&mut self, result: Result<()>) { let result = ErrorCode::result_to_c(result); #[cfg(pam_impl = "LinuxPam")] let result = result | libpam_sys::PAM_DATA_SILENT; unsafe { - libpam_sys::pam_end(me.raw_mut(), result); + libpam_sys::pam_end(self.inner_mut(), result); } } - /// Consumes this and gives you back the raw PAM handle. - pub fn into_inner(self) -> NonNull<libpam_sys::pam_handle> { - let me = ManuallyDrop::new(self); - me.0 - } - /// Gets a reference to the inner PAM handle. - pub fn raw_ref(&self) -> &libpam_sys::pam_handle { - unsafe { self.0.as_ref() } + pub fn inner(&self) -> &libpam_sys::pam_handle { + &self.0 } /// Gets a mutable reference to the inner PAM handle. - pub fn raw_mut(&mut self) -> &mut libpam_sys::pam_handle { - unsafe { self.0.as_mut() } - } -} - -impl Drop for LibPamHandle { - fn drop(&mut self) { - unsafe { libpam_sys::pam_end(self.0.as_mut(), 0) }; + pub fn inner_mut(&mut self) -> &mut libpam_sys::pam_handle { + &mut self.0 } } @@ -344,12 +327,7 @@ // SAFETY: We're calling this function with a known value. #[cfg(pam_impl = "LinuxPam")] unsafe { - libpam_sys::pam_syslog( - self.raw_ref(), - level, - b"%s\0".as_ptr().cast(), - entry.as_ptr(), - ) + libpam_sys::pam_syslog(self.inner(), level, b"%s\0".as_ptr().cast(), entry.as_ptr()) } #[cfg(pam_impl = "Sun")] unsafe { @@ -384,7 +362,7 @@ let mut output: *const c_char = ptr::null(); let ret = unsafe { libpam_sys::pam_get_user( - self.raw_mut(), + self.inner_mut(), &mut output, memory::prompt_ptr(prompt.as_deref()), ) @@ -440,7 +418,7 @@ let mut ptr: *const c_void = ptr::null(); unsafe { ErrorCode::result_from(libpam_sys::pam_get_data( - self.raw_ref(), + self.inner(), full_key.as_ptr(), &mut ptr, )) @@ -455,7 +433,7 @@ let data = Box::new(data); ErrorCode::result_from(unsafe { libpam_sys::pam_set_data( - self.raw_mut(), + self.inner_mut(), full_key.as_ptr(), Box::into_raw(data).cast(), drop_module_data::<T>, @@ -504,7 +482,7 @@ // SAFETY: We're calling this with known-good values. let res = unsafe { libpam_sys::pam_get_authtok( - self.raw_mut(), + self.inner_mut(), item_type.into(), &mut output, memory::prompt_ptr(prompt.as_deref()), @@ -525,7 +503,7 @@ let mut output: *mut c_char = ptr::null_mut(); let result = unsafe { libpam_sys::__pam_get_authtok( - self.raw_mut(), + self.inner_mut(), libpam_sys::PAM_HANDLE, item_type.into(), ptr::null(), @@ -543,7 +521,7 @@ let prompt = memory::option_cstr_os(prompt); let result = unsafe { libpam_sys::__pam_get_authtok( - self.raw_mut(), + self.inner_mut(), libpam_sys::PAM_PROMPT, item_type.into(), memory::prompt_ptr(prompt.as_deref()), @@ -559,15 +537,12 @@ /// Gets the `PAM_CONV` item from the handle. fn conversation_item(&self) -> Result<&PamConv> { - let output: *const PamConv = ptr::null_mut(); + let mut output: *const c_void = ptr::null(); let result = unsafe { - libpam_sys::pam_get_item( - self.raw_ref(), - ItemType::Conversation.into(), - &mut output.cast(), - ) + libpam_sys::pam_get_item(self.inner(), ItemType::Conversation.into(), &mut output) }; ErrorCode::result_from(result)?; + let output: *const PamConv = output.cast(); // SAFETY: We got this result from PAM, and we're checking if it's null. unsafe { output.as_ref() }.ok_or(ErrorCode::ConversationError) }
--- a/src/libpam/items.rs Mon Jul 14 15:07:16 2025 -0400 +++ b/src/libpam/items.rs Mon Jul 14 17:40:11 2025 -0400 @@ -58,12 +58,9 @@ /// # Safety /// /// You better be requesting an item which is a C string. -pub unsafe fn get_cstr_item( - hdl: &LibPamHandle, - item_type: ItemType, -) -> crate::Result<Option<OsString>> { +pub unsafe fn get_cstr_item(hdl: &LibPamHandle, item_type: ItemType) -> Result<Option<OsString>> { let mut output = ptr::null(); - let ret = unsafe { libpam_sys::pam_get_item(hdl.raw_ref(), item_type as c_int, &mut output) }; + let ret = unsafe { libpam_sys::pam_get_item(hdl.inner(), item_type as c_int, &mut output) }; ErrorCode::result_from(ret)?; Ok(memory::copy_pam_string(output.cast())) } @@ -77,11 +74,11 @@ hdl: &mut LibPamHandle, item_type: ItemType, data: Option<&OsStr>, -) -> crate::Result<()> { +) -> Result<()> { let data_str = memory::option_cstr_os(data); let ret = unsafe { libpam_sys::pam_set_item( - hdl.raw_mut(), + hdl.inner_mut(), item_type as c_int, memory::prompt_ptr(data_str.as_deref()).cast(), )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testharness/install-test-harness.sh Mon Jul 14 17:40:11 2025 -0400 @@ -0,0 +1,13 @@ +#!/bin/bash + +set -eo pipefail + +HERE="$(dirname -- "$0")" +echo "$HERE" +cargo build --release + +sudo mkdir -p /lib/security +sudo cp ../target/release/libnonstick_testharness.so /lib/security/pam_testharness.so +sudo cp nonstick_testharness.conf /etc/pam.d/nonstick-testharness +trap 'sudo rm /etc/pam.d/nonstick-testharness; sudo rm /lib/security/pam_testharness.so' EXIT +"$@" && echo "SUCCESS!!!" || echo "FAILURE: $?" \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testharness/nonstick_testharness.conf Mon Jul 14 17:40:11 2025 -0400 @@ -0,0 +1,5 @@ +# PAM configuration file for nonstick_testharness +auth required pam_testharness.so param param2 +account required pam_testharness.so +password required pam_testharness.so +session required pam_testharness.so
--- a/testharness/src/bin/testharness.rs Mon Jul 14 15:07:16 2025 -0400 +++ b/testharness/src/bin/testharness.rs Mon Jul 14 17:40:11 2025 -0400 @@ -1,3 +1,121 @@ //! The actual program which runs the tests. -fn main() {} +use nonstick::conv::Exchange; +use nonstick::items::Items; +use nonstick::libpam::TransactionBuilder; +use nonstick::{Conversation, ErrorCode, Flags, LibPamTransaction, PamShared, Transaction}; +use std::cell::Cell; +use std::ffi::OsString; +use std::os::unix::ffi::OsStrExt; + +fn main() { + test_wrong_user(); + test_wrong_password(); + test_correct(); +} + +#[derive(Debug, Default)] +struct TestHarness { + username_requested: Cell<bool>, + wrong_username: bool, + wrong_password: bool, + changing_password: Cell<bool>, + change_prompt_count: Cell<u8>, +} + +impl Conversation for &TestHarness { + fn communicate(&self, messages: &[Exchange]) { + if let [only_msg] = messages { + match only_msg { + Exchange::Prompt(p) => { + if self.username_requested.get() { + panic!("username already requested!") + } + if self.wrong_username { + p.set_answer(Ok(OsString::from("not-right"))) + } else { + p.set_answer(Ok(OsString::from("initial"))) + } + self.username_requested.set(true) + } + Exchange::MaskedPrompt(p) => { + let answer = if self.changing_password.get() { + let prompts = self.change_prompt_count.get(); + self.change_prompt_count.set(prompts + 1); + match prompts { + 0 => "mistake", + 1 => "mismatch", + 2 => "acceptable", + 3 => "acceptable", + _ => panic!("unexpected number of prompts!"), + } + } else if self.wrong_password { + "bogus" + } else { + "valid" + }; + p.set_answer(Ok(OsString::from(answer))); + } + Exchange::Error(e) if self.changing_password.get() => e.set_answer(Ok(())), + other => panic!("Unknown message {other:?}!"), + } + } else { + for msg in messages { + match msg { + Exchange::Info(i) => i.set_answer(Ok(())), + Exchange::Error(e) => e.set_answer(Ok(())), + Exchange::Prompt(p) => match p.question().as_bytes() { + b"How many?" => p.set_answer(Ok(OsString::from("123"))), + _ => p.set_answer(Err(ErrorCode::ConversationError)), + }, + Exchange::MaskedPrompt(p) => match p.question().as_bytes() { + b"Where?" => p.set_answer(Ok(OsString::from("abc"))), + _ => p.set_answer(Err(ErrorCode::ConversationError)), + }, + other => other.set_error(ErrorCode::Abort), + } + } + } + } +} + +impl TestHarness { + fn start(&self) -> LibPamTransaction<&Self> { + TransactionBuilder::new_with_service("nonstick-testharness") + .build(self) + .expect("expected build success") + } +} + +fn test_wrong_user() { + let harness = TestHarness { + wrong_username: true, + ..Default::default() + }; + let mut tx = harness.start(); + let auth = tx.authenticate(Flags::empty()); + assert_eq!(auth, Err(ErrorCode::UserUnknown)); +} + +fn test_wrong_password() { + let harness = TestHarness { + wrong_password: true, + ..Default::default() + }; + let mut tx = harness.start(); + let auth = tx.authenticate(Flags::empty()); + assert_eq!(auth, Err(ErrorCode::AuthenticationError)); +} + +fn test_correct() { + let harness = TestHarness::default(); + let mut tx = harness.start(); + tx.authenticate(Flags::empty()).unwrap(); + assert_eq!(tx.items().user().unwrap().unwrap(), "updated-in-process"); + let result = tx.account_management(Flags::empty()); + assert_eq!(result, Err(ErrorCode::NewAuthTokRequired)); + harness.changing_password.set(true); + let change = tx.change_authtok(Flags::CHANGE_EXPIRED_AUTHTOK); + assert_eq!(change, Err(ErrorCode::TryAgain)); + tx.change_authtok(Flags::CHANGE_EXPIRED_AUTHTOK).unwrap(); +}
--- a/testharness/src/lib.rs Mon Jul 14 15:07:16 2025 -0400 +++ b/testharness/src/lib.rs Mon Jul 14 17:40:11 2025 -0400 @@ -1,26 +1,103 @@ //! The nonstick library + +use crate::nonstick::items::ItemsMut; +use std::cell::Cell; extern crate nonstick; -use nonstick::{pam_hooks, Flags, ModuleClient, PamModule}; -use std::ffi::CStr; +use nonstick::conv::{ErrorMsg, InfoMsg, MaskedQAndA, QAndA}; +use nonstick::{error, info, pam_hooks, ErrorCode, Flags, ModuleClient, PamModule}; +use std::ffi::{CStr, OsString}; +use std::os::unix::ffi::OsStrExt; struct TestHarness; impl<M: ModuleClient> PamModule<M> for TestHarness { - fn authenticate(_handle: &mut M, _args: Vec<&CStr>, _flags: Flags) -> nonstick::Result<()> { - Ok(()) + fn authenticate(handle: &mut M, args: Vec<&CStr>, _: Flags) -> nonstick::Result<()> { + let strings: Vec<_> = args.iter().map(|&a| Vec::from(a.to_bytes())).collect(); + if strings != vec![Vec::from(b"param"), Vec::from(b"param2")] { + return Err(ErrorCode::SystemError); + } + let username = handle.username(None)?; + if username != "initial" { + return Err(ErrorCode::UserUnknown); + } + handle + .items_mut() + .set_user(Some("updated-in-process".as_ref()))?; + handle.set_module_data("florgus", Cell::new(99))?; + let authtok = handle.authtok(Some("custom".as_ref()))?; + if authtok.as_bytes() != b"valid" { + return Err(ErrorCode::AuthenticationError); + } + let info = InfoMsg::new("Watch out!".as_ref()); + let err = ErrorMsg::new("It's broken!".as_ref()); + let public = QAndA::new("How many?".as_ref()); + let private = MaskedQAndA::new("Where?".as_ref()); + let msgs = &[ + info.exchange(), + err.exchange(), + public.exchange(), + private.exchange(), + ]; + handle.communicate(msgs); + let public = public.answer()?; + info!(handle, "public question: {:?}", public); + let private = private.answer()?; + info!(handle, "private question: {:?}", private); + if public.as_bytes() == b"123" && private.as_bytes() == b"abc" { + Ok(()) + } else { + Err(ErrorCode::Abort) + } } - fn account_management( - _handle: &mut M, - _args: Vec<&CStr>, - _flags: Flags, - ) -> nonstick::Result<()> { - Ok(()) + fn account_management(handle: &mut M, _: Vec<&CStr>, _: Flags) -> nonstick::Result<()> { + let value: &Cell<i32> = match handle.username(None)?.as_bytes() { + b"initial" => return Err(ErrorCode::AccountExpired), + b"updated-in-process" => handle.get_module_data("florgus"), + _ => return Err(ErrorCode::UserUnknown), + } + .ok_or(ErrorCode::SessionError)?; + let florgus_str: Option<&i32> = handle.get_module_data("florgus"); + if let Some(s) = florgus_str { + error!( + handle, + "module_data type mismatch: florgus = <{s}> but should not be set" + ) + } + if value.get() != 99 { + error!(handle, "wrong value! {}", value.get()); + return Err(ErrorCode::AuthTokError); + } + let password = handle.authtok(None)?; + if password.as_bytes() == b"valid" { + Err(ErrorCode::NewAuthTokRequired) + } else { + Ok(()) + } } - fn change_authtok(_handle: &mut M, _args: Vec<&CStr>, _flags: Flags) -> nonstick::Result<()> { - todo!() + fn change_authtok(handle: &mut M, _: Vec<&CStr>, flags: Flags) -> nonstick::Result<()> { + if flags.contains(Flags::PRELIMINARY_CHECK) { + let password = handle.authtok(None)?; + if password.as_bytes() != b"acceptable" { + return Err(ErrorCode::PermissionDenied); + } + handle.set_module_data("checked_pass", password) + } else if flags.contains(Flags::UPDATE_AUTHTOK) { + let password = handle.authtok(None)?; + let checked: &OsString = handle + .get_module_data("checked_pass") + .ok_or(ErrorCode::SystemError)?; + if password != *checked { + error!(handle, "password mismatch? {password:?} {checked:?}"); + return Err(ErrorCode::AuthenticationError); + } + Ok(()) + } else { + error!(handle, "invalid flag state: {flags:?}"); + Err(ErrorCode::SystemError) + } } }