view testharness/src/lib.rs @ 183:4f46681b3f54 default tip

Catch a few stray cargo fmt things.
author Paul Fisher <paul@pfish.zone>
date Wed, 30 Jul 2025 18:43:07 -0400
parents 346dc13724ce
children
line wrap: on
line source

//! The nonstick library

use crate::nonstick::items::ItemsMut;
use crate::nonstick::EnvironMapMut;
use std::cell::Cell;
extern crate nonstick;

use nonstick::conv::{ErrorMsg, InfoMsg, MaskedQAndA, QAndA};
use nonstick::{
    error, info, pam_export, AuthnFlags, AuthtokAction, AuthtokFlags, ErrorCode, ModuleClient,
    PamModule,
};
use std::ffi::CStr;
use std::os::unix::ffi::OsStrExt;

struct TestHarness;

impl<M: ModuleClient> PamModule<M> for TestHarness {
    fn authenticate(handle: &mut M, args: Vec<&CStr>, _: AuthnFlags) -> 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, _: Vec<&CStr>, _: AuthnFlags) -> nonstick::Result<()> {
        match handle.username(None)?.as_bytes() {
            b"initial" => return Err(ErrorCode::AccountExpired),
            b"updated-in-process" => (),
            _ => return Err(ErrorCode::UserUnknown),
        };
        let value: &Cell<i32> = handle
            .get_module_data("florgus")
            .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);
        }
        handle.environ_mut().insert("nin", "nine inch nails");
        let password = handle.authtok(None)?;
        if password.as_bytes() == b"valid" {
            Err(ErrorCode::NewAuthTokRequired)
        } else {
            Ok(())
        }
    }

    fn change_authtok(
        handle: &mut M,
        _: Vec<&CStr>,
        action: AuthtokAction,
        _flags: AuthtokFlags,
    ) -> nonstick::Result<()> {
        match action {
            AuthtokAction::Validate => {
                let expected: &[u8] = if cfg!(pam_impl = "Sun") {
                    b"valid"
                } else {
                    b"old token!"
                };
                if handle.old_authtok(None)?.as_bytes() != expected {
                    return Err(ErrorCode::AuthenticationError);
                }
                Ok(())
            }
            AuthtokAction::Update => {
                let password = handle.authtok(None)?;
                if password.as_bytes() != b"acceptable" {
                    return Err(ErrorCode::PermissionDenied);
                }
                Ok(())
            }
        }
    }
}

pam_export!(TestHarness);