changeset 45:ce47901aab7a

Rename to “nonstick”, move to root, update docs and license. - Renames the crate to “nonstick”. - Moves the main library to the root of the repository. - Removes the example PAM modules. - Updates copyright information in LICENSE file. - Updates the README.
author Paul Fisher <paul@pfish.zone>
date Tue, 15 Apr 2025 00:50:23 -0400
parents 50371046c61a
children 350579171e1f
files .github/workflows/build.yml .github/workflows/check.yml .gitignore Cargo.toml LICENSE README.md pam-http/Cargo.toml pam-http/Justfile pam-http/README.md pam-http/conf/http-auth pam-http/src/lib.rs pam-http/test.c pam-sober/Cargo.toml pam-sober/Justfile pam-sober/conf/sober-auth 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 src/constants.rs src/conv.rs src/items.rs src/lib.rs src/macros.rs src/module.rs
diffstat 30 files changed, 763 insertions(+), 1242 deletions(-) [+]
line wrap: on
line diff
--- a/.github/workflows/build.yml	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-on:
-  push:
-    branches:
-      - master
-  workflow_call:
-
-name: Build & publish
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout sources
-        uses: actions/checkout@v2
-      - name: Install PAM dev files
-        run: sudo apt-get install -y libpam0g-dev
-      - name: Use cargo cache
-        uses: actions/cache@v2
-        with:
-          path: |
-            ~/.cargo/bin/
-            ~/.cargo/registry/index/
-            ~/.cargo/registry/cache/
-            ~/.cargo/git/db/
-            target/
-          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
-      - name: Install stable toolchain
-        uses: actions-rs/toolchain@v1
-        with:
-          profile: minimal
-          toolchain: stable
-          override: true
-      - name: Publish module
-        uses: katyo/publish-crates@v1
-        with:
-          registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
-          check-repo: ${{ github.event_name == 'push' }}
-          ignore-unpublished-changes: true
-          # Only publish pam module, as pam-http and pam-sober are example projects
-          path: pam
-          args: --package pam-bindings
\ No newline at end of file
--- a/.github/workflows/check.yml	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-on:
-  pull_request:
-    branches:
-      - master
-  workflow_call:
-
-name: Check
-
-jobs:
-  check:
-    name: Check & Lint
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout sources
-        uses: actions/checkout@v2
-
-      - name: Install PAM dev files
-        run: sudo apt-get install -y libpam0g-dev
-
-      - name: Use cargo cache
-        uses: actions/cache@v2
-        with:
-          path: |
-            ~/.cargo/bin/
-            ~/.cargo/registry/index/
-            ~/.cargo/registry/cache/
-            ~/.cargo/git/db/
-            target/
-          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
-
-      - name: Install stable toolchain
-        uses: actions-rs/toolchain@v1
-        with:
-          profile: minimal
-          toolchain: stable
-          override: true
-
-      - name: Run cargo check
-        uses: actions-rs/cargo@v1
-        with:
-          command: check
-          args: --manifest-path pam/Cargo.toml
-
-      - name: Run cargo test
-        uses: actions-rs/cargo@v1
-        with:
-          command: test
-          args: --manifest-path pam/Cargo.toml
-
-      - name: Run cargo check on pam-http
-        uses: actions-rs/cargo@v1
-        with:
-          command: check
-          args: --manifest-path pam-http/Cargo.toml
-
-      - name: Run cargo check on pam-sober
-        uses: actions-rs/cargo@v1
-        with:
-          command: check
-          args: --manifest-path pam-sober/Cargo.toml
-
-      - name: Run cargo fmt
-        uses: actions-rs/cargo@v1
-        with:
-          command: fmt
-          args: --manifest-path pam/Cargo.toml --all --check
-
-      - name: Run cargo clippy
-        uses: actions-rs/cargo@v1
-        with:
-          command: clippy
-          args: --manifest-path pam/Cargo.toml -- -D warnings
\ No newline at end of file
--- a/.gitignore	Sat Mar 08 19:29:46 2025 -0500
+++ b/.gitignore	Tue Apr 15 00:50:23 2025 -0400
@@ -1,15 +1,3 @@
-# Compiled files
-*.o
-*.so
-*.rlib
-*.dll
-
-# Executables
-*.exe
-
-# Generated by Cargo
-target/
-Cargo.lock
-
-# Override top-level .gitignore in Misc repo
-!src/tozny
+/target
+/.idea
+/Cargo.lock
\ No newline at end of file
--- a/Cargo.toml	Sat Mar 08 19:29:46 2025 -0500
+++ b/Cargo.toml	Tue Apr 15 00:50:23 2025 -0400
@@ -1,2 +1,15 @@
-[workspace]
-members = ["pam", "pam-sober", "pam-http"]
+[package]
+name = "nonstick"
+description = "PAM bindings for Rust"
+version = "0.0.0-pre"
+authors = ["Paul Fisher <paul@pfish.zone>", "Anthony Nowell <anowell@gmail.com>" ]
+repository = "https://hg.pfish.zone/crates/nonstick/"
+readme = "README.md"
+keywords = ["pam", "ffi", "linux", "authentication"]
+license = "MIT"
+
+[lib]
+name = "pam"
+
+[dependencies]
+libc = "0.2.97"
--- a/LICENSE	Sat Mar 08 19:29:46 2025 -0500
+++ b/LICENSE	Tue Apr 15 00:50:23 2025 -0400
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2015 TOZNY
+Copyright © 2015 TOZNY, 2015–2022 pam-rs contributors, 2025 Paul Fisher
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
--- a/README.md	Sat Mar 08 19:29:46 2025 -0500
+++ b/README.md	Tue Apr 15 00:50:23 2025 -0400
@@ -1,25 +1,26 @@
-pam-rs
-========
+# 🍳 nonstick
 
-Rust interface to the pluggable authentication module framework (PAM).
+Nonstick lets you use PAM (Pluggable Authentication Modules) from Rust without having to deal with icky unsafe code.
+
+## Status
 
-The goal of this library is to provide a type-safe API that can be used to
-interact with PAM.  The library is incomplete - currently it supports a subset
-of functions for use in a pam authentication module.  A pam module is a shared
-library that is invoked to authenticate a user, or to perform other functions.
+It is currently very incomplete.
+It only provides functionality for developing your own PAM authentication module (i.e., a backend that PAM calls to authenticate a user or do something similar).
+At the moment, [Linux-PAM](https://github.com/linux-pam/linux-pam) is the only supported PAM implementation.
 
-## 🌐 [pam-http](pam-http)
+I will make an effort not to break APIs with development, but consider it alpha, pre-1.0 software.
+While the code itself should be _secure_ and mostly safe, the API may not be completely stable.
 
-An example of using pam-rs by performing HTTP basic access auth to authenticate users.
+Goals include:
 
-## 🍻 [pam-sober](pam-sober)
+- Bindings for PAM clients.
+- Support for non–Linux-PAM implementations.
 
-If you aren't sober enough for basic math, you can't login!
+## Credits
 
-### Credits
-
-The contents of this repo are heavily borrowed from:
+This is a direct fork of [Anthony Nowell](http://anowell.com/)’s [`pam-rs`/`pam-bindings` crate](https://crates.io/crates/pam-bindings).
+`pam-rs` was in turn inspired by:
 
-- [tozny/rust-pam](https://github.com/tozny/rust-pam)
-- [ndenev/pam_groupmap](https://github.com/ndenev/pam_groupmap)
-- [beatgammit/pam-http](https://github.com/beatgammit/pam-http)
+- [`rust-pam` by tozny](https://github.com/tozny/rust-pam)
+- [`pam_groupmap` by ndenev](https://github.com/ndenev/pam_groupmap)
+- [`pam-http` by beatgammit](https://github.com/beatgammit/pam-http)
--- a/pam-http/Cargo.toml	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-[package]
-name = "pam-http"
-version = "0.1.0"
-authors = ["Anthony Nowell <anowell@gmail.com>"]
-
-[lib]
-name = "pam_http"
-crate-type = ["cdylib"]
-
-[dependencies]
-pam-bindings = { path = "../pam/" }
-reqwest = { version = "0.11.3", features = ["blocking"] }
--- a/pam-http/Justfile	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-
-all:
-    cargo build
-
-install:
-    @cargo build --release
-    sudo cp conf/http-auth /etc/pam.d/
-    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
--- a/pam-http/README.md	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-pam-http
-========
-
-A PAM HTTP BasicAuth module built using pam-rs
-
-# Prerequisites
-
-You need some libraries before you build like libpam and libssl.
-
-If you're going to build on Ubuntu, just run this:
-
-```
-sudo apt-get install -y build-essential libpam0g-dev libpam0g libssl-dev
-```
-
-# Building
-
-Just use `cargo build`.
-
-# Usage
-
-You need to move the build product to a folder where PAM is looking for modules.
-
-If you're using Ubuntu you can move `libpam_http.so` to `/lib/security`.
-After doing so you need to make sure it has proper permissions: `sudo chmod 755 /lib/security/libpam_http.so`.
-Then you can place a configuration file in `/etc/pam.d/`. It can look something like this:
-
-```
-auth sufficient libpam_http.so url=https://theserver.example.com/someendpoint
-account sufficient libpam_http.so
-```
-
-Make sure the endpoint you're specifying can receive GET requests and supports 
-[HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication#Client_side). 
-If the user is authenticated successfully it should return HTTP 200.
--- a/pam-http/conf/http-auth	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-auth sufficient pam_http.so url=http://localhost:3000
-account sufficient pam_http.so
--- a/pam-http/src/lib.rs	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-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 reqwest::blocking::Client;
-use reqwest::StatusCode;
-use std::collections::HashMap;
-use std::ffi::CStr;
-use std::time::Duration;
-use pam::pam_try;
-
-struct PamHttp;
-pam::pam_hooks!(PamHttp);
-
-impl PamHooks for PamHttp {
-    // This function performs the task of authenticating the user.
-    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())
-            .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));
-
-        let url: &str = match args.get("url") {
-            Some(url) => url,
-            None => return PamResultCode::PAM_AUTH_ERR,
-        };
-
-        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),
-            PamResultCode::PAM_AUTH_ERR
-        );
-
-        if !status.is_success() {
-            println!("HTTP Error: {}", status);
-            return PamResultCode::PAM_AUTH_ERR;
-        }
-
-        PamResultCode::PAM_SUCCESS
-    }
-
-    fn sm_setcred(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
-        println!("set credentials");
-        PamResultCode::PAM_SUCCESS
-    }
-
-    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)
-        .basic_auth(user, password)
-        .send()
-        .map(|r| r.status())
-}
--- a/pam-http/test.c	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-#include <security/pam_appl.h>
-#include <security/pam_misc.h>
-#include <stdio.h>
-
-const struct pam_conv conv = {
-	misc_conv,
-	NULL
-};
-
-int main(int argc, char *argv[]) {
-	pam_handle_t* pamh = NULL;
-	int retval;
-	const char* user = "nobody";
-
-	if(argc != 2) {
-		printf("Usage: app [username]\n");
-		exit(1);
-	}
-
-	user = argv[1];
-
-	retval = pam_start("http-auth", user, &conv, &pamh);
-
-	// Are the credentials correct?
-	if (retval == PAM_SUCCESS) {
-		printf("Credentials accepted.\n");
-		retval = pam_authenticate(pamh, 0);
-	}
-
-	// Can the accound be used at this time?
-	if (retval == PAM_SUCCESS) {
-		printf("Account is valid.\n");
-		retval = pam_acct_mgmt(pamh, 0);
-	}
-
-	// Did everything work?
-	if (retval == PAM_SUCCESS) {
-		printf("Authenticated\n");
-	} else {
-		printf("Not Authenticated\n");
-	}
-
-	// close PAM (end session)
-	if (pam_end(pamh, retval) != PAM_SUCCESS) {
-		pamh = NULL;
-		printf("check_user: failed to release authenticator\n");
-		exit(1);
-	}
-
-	return retval == PAM_SUCCESS ? 0 : 1;
-}
-
--- a/pam-sober/Cargo.toml	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-[package]
-name = "pam-sober"
-version = "0.1.0"
-authors = ["Anthony Nowell <anowell@gmail.com>"]
-
-[lib]
-name = "pam_sober"
-crate-type = ["cdylib"]
-
-[dependencies]
-pam-bindings = { path = "../pam/" }
-rand = "0.8.4"
--- a/pam-sober/Justfile	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-
-all:
-    cargo build
-
-install:
-    @cargo build --release
-    sudo cp conf/sober-auth /etc/pam.d/
-    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
--- a/pam-sober/conf/sober-auth	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-auth sufficient pam_sober.so
-account sufficient pam_sober.so
--- a/pam-sober/src/lib.rs	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-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 rand::Rng;
-use std::ffi::CStr;
-use std::str::FromStr;
-use pam::pam_try;
-
-struct PamSober;
-pam::pam_hooks!(PamSober);
-
-impl PamHooks for PamSober {
-    // This function performs the task of authenticating the user.
-    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 ;-)
-        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));
-
-        let conv = match pamh.get_item::<Conv>() {
-            Ok(Some(conv)) => conv,
-            Ok(None) => todo!(),
-            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);
-
-        // This println kinda helps debugging since the test script doesn't echo
-        eprintln!("[DEBUG]: {}{}", math, a + b);
-
-        let password = pam_try!(conv.send(PAM_PROMPT_ECHO_ON, &math));
-
-        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
-        }
-    }
-
-    fn sm_setcred(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
-        println!("set credentials");
-        PamResultCode::PAM_SUCCESS
-    }
-
-    fn acct_mgmt(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
-        println!("account management");
-        PamResultCode::PAM_SUCCESS
-    }
-}
--- a/pam-sober/test.c	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-#include <security/pam_appl.h>
-#include <security/pam_misc.h>
-#include <stdio.h>
-
-const struct pam_conv conv = {
-	misc_conv,
-	NULL
-};
-
-int main(int argc, char *argv[]) {
-	pam_handle_t* pamh = NULL;
-	int retval;
-	const char* user = "nobody";
-
-	if(argc != 2) {
-		printf("Usage: app [username]\n");
-		exit(1);
-	}
-
-	user = argv[1];
-
-	retval = pam_start("sober-auth", user, &conv, &pamh);
-
-	// Are the credentials correct?
-	if (retval == PAM_SUCCESS) {
-		printf("PAM module initialized\n");
-		retval = pam_authenticate(pamh, 0);
-	}
-
-	// Can the accound be used at this time?
-	if (retval == PAM_SUCCESS) {
-		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");
-	}
-
-	// close PAM (end session)
-	if (pam_end(pamh, retval) != PAM_SUCCESS) {
-		pamh = NULL;
-		printf("check_user: failed to release authenticator\n");
-		exit(1);
-	}
-
-	return retval == PAM_SUCCESS ? 0 : 1;
-}
-
--- a/pam/Cargo.toml	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-[package]
-
-name = "pam-bindings"
-description = "PAM bindings for Rust"
-version = "0.1.1"
-authors = [ "Anthony Nowell <anowell@gmail.com>" ]
-repository = "https://github.com/anowell/pam-rs"
-readme = "../README.md"
-keywords = ["pam", "ffi", "linux", "authentication"]
-license = "MIT"
-
-[lib]
-name = "pam"
-
-[dependencies]
-libc = "0.2.97"
--- a/pam/src/constants.rs	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-use libc::{c_int, c_uint};
-
-// TODO: Import constants from C header file at compile time.
-
-pub type PamFlag = c_uint;
-pub type PamItemType = c_int;
-pub type PamMessageStyle = c_int;
-
-// The Linux-PAM flags
-// see /usr/include/security/_pam_types.h
-pub const PAM_SILENT: PamFlag = 0x8000;
-pub const PAM_DISALLOW_NULL_AUTHTOK: PamFlag = 0x0001;
-pub const PAM_ESTABLISH_CRED: PamFlag = 0x0002;
-pub const PAM_DELETE_CRED: PamFlag = 0x0004;
-pub const PAM_REINITIALIZE_CRED: PamFlag = 0x0008;
-pub const PAM_REFRESH_CRED: PamFlag = 0x0010;
-pub const PAM_CHANGE_EXPIRED_AUTHTOK: PamFlag = 0x0020;
-
-// Message styles
-pub const PAM_PROMPT_ECHO_OFF: PamMessageStyle = 1;
-pub const PAM_PROMPT_ECHO_ON: PamMessageStyle = 2;
-pub const PAM_ERROR_MSG: PamMessageStyle = 3;
-pub const PAM_TEXT_INFO: PamMessageStyle = 4;
-/// yes/no/maybe conditionals
-pub const PAM_RADIO_TYPE: PamMessageStyle = 5;
-pub const PAM_BINARY_PROMPT: PamMessageStyle = 7;
-
-// The Linux-PAM return values
-// see /usr/include/security/_pam_types.h
-#[allow(non_camel_case_types, dead_code)]
-#[derive(Debug, PartialEq)]
-#[repr(C)]
-pub enum PamResultCode {
-    PAM_SUCCESS = 0,
-    PAM_OPEN_ERR = 1,
-    PAM_SYMBOL_ERR = 2,
-    PAM_SERVICE_ERR = 3,
-    PAM_SYSTEM_ERR = 4,
-    PAM_BUF_ERR = 5,
-    PAM_PERM_DENIED = 6,
-    PAM_AUTH_ERR = 7,
-    PAM_CRED_INSUFFICIENT = 8,
-    PAM_AUTHINFO_UNAVAIL = 9,
-    PAM_USER_UNKNOWN = 10,
-    PAM_MAXTRIES = 11,
-    PAM_NEW_AUTHTOK_REQD = 12,
-    PAM_ACCT_EXPIRED = 13,
-    PAM_SESSION_ERR = 14,
-    PAM_CRED_UNAVAIL = 15,
-    PAM_CRED_EXPIRED = 16,
-    PAM_CRED_ERR = 17,
-    PAM_NO_MODULE_DATA = 18,
-    PAM_CONV_ERR = 19,
-    PAM_AUTHTOK_ERR = 20,
-    PAM_AUTHTOK_RECOVERY_ERR = 21,
-    PAM_AUTHTOK_LOCK_BUSY = 22,
-    PAM_AUTHTOK_DISABLE_AGING = 23,
-    PAM_TRY_AGAIN = 24,
-    PAM_IGNORE = 25,
-    PAM_ABORT = 26,
-    PAM_AUTHTOK_EXPIRED = 27,
-    PAM_MODULE_UNKNOWN = 28,
-    PAM_BAD_ITEM = 29,
-    PAM_CONV_AGAIN = 30,
-    PAM_INCOMPLETE = 31,
-}
--- a/pam/src/conv.rs	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-use libc::{c_char, c_int};
-use std::ffi::{CStr, CString};
-use std::ptr;
-
-use constants::PamMessageStyle;
-use constants::PamResultCode;
-use items::Item;
-use module::PamResult;
-
-#[repr(C)]
-struct PamMessage {
-    msg_style: PamMessageStyle,
-    msg: *const c_char,
-}
-
-#[repr(C)]
-struct PamResponse {
-    resp: *const c_char,
-    resp_retcode: libc::c_int, // Unused - always zero
-}
-
-/// `PamConv` acts as a channel for communicating with user.
-///
-/// Communication is mediated by the pam client (the application that invoked
-/// pam).  Messages sent will be relayed to the user by the client, and response
-/// will be relayed back.
-#[repr(C)]
-pub struct Inner {
-    conv: extern "C" fn(
-        num_msg: c_int,
-        pam_message: &&PamMessage,
-        pam_response: &mut *const PamResponse,
-        appdata_ptr: *const libc::c_void,
-    ) -> PamResultCode,
-    appdata_ptr: *const libc::c_void,
-}
-
-pub struct Conv<'a>(&'a Inner);
-
-impl Conv<'_> {
-    /// Sends a message to the pam client.
-    ///
-    /// This will typically result in the user seeing a message or a prompt.
-    /// There are several message styles available:
-    ///
-    /// - PAM_PROMPT_ECHO_OFF
-    /// - PAM_PROMPT_ECHO_ON
-    /// - PAM_ERROR_MSG
-    /// - PAM_TEXT_INFO
-    /// - PAM_RADIO_TYPE
-    /// - PAM_BINARY_PROMPT
-    ///
-    /// 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<&CStr>> {
-        let mut resp_ptr: *const PamResponse = ptr::null();
-        let msg_cstr = CString::new(msg).unwrap();
-        let msg = PamMessage {
-            msg_style: style,
-            msg: msg_cstr.as_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
-            let response = unsafe { (*resp_ptr).resp };
-            if response.is_null() {
-                Ok(None)
-            } else {
-                Ok(Some(unsafe { CStr::from_ptr(response) }))
-            }
-        } else {
-            Err(ret)
-        }
-    }
-}
-
-impl Item for Conv<'_> {
-    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	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-#[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,
-}
-
-// 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;
-
-    /// 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;
-
-    /// The function to convert from this wrapper type to a C-compatible pointer.
-    fn into_raw(self) -> *const Self::Raw;
-}
-
-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<'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()
-            }
-        }
-    };
-}
-
-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	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-//! Interface to the pluggable authentication module framework (PAM).
-//!
-//! The goal of this library is to provide a type-safe API that can be used to
-//! interact with PAM.  The library is incomplete - currently it supports
-//! a subset of functions for use in a pam authentication module.  A pam module
-//! is a shared library that is invoked to authenticate a user, or to perform
-//! other functions.
-//!
-//! For general information on writing pam modules, see
-//! [The Linux-PAM Module Writers' Guide][module-guide]
-//!
-//! [module-guide]: http://www.linux-pam.org/Linux-PAM-html/Linux-PAM_MWG.html
-//!
-//! A typical authentication module will define an external function called
-//! `pam_sm_authenticate()`, which will use functions in this library to
-//! interrogate the program that requested authentication for more information,
-//! and to render a result.  For a working example that uses this library, see
-//! [toznyauth-pam][].
-//!
-//! [toznyauth-pam]: https://github.com/tozny/toznyauth-pam
-//!
-//! Note that constants that are normally read from pam header files are
-//! hard-coded in the `constants` module.  The values there are taken from
-//! a Linux system.  That means that it might take some work to get this library
-//! to work on other platforms.
-
-extern crate libc;
-
-pub mod constants;
-pub mod conv;
-pub mod items;
-#[doc(hidden)]
-pub mod macros;
-pub mod module;
--- a/pam/src/macros.rs	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-/// Macro to generate the `extern "C"` entrypoint bindings needed by PAM
-///
-/// You can call `pam_hooks!(SomeType);` for any type that implements `PamHooks`
-///
-/// ## Examples:
-///
-/// Here is full example of a PAM module that would authenticate and authorize everybody:
-///
-/// ```
-/// #[macro_use] extern crate pam;
-///
-/// use pam::module::{PamHooks, PamHandle};
-/// use pam::constants::{PamResultCode, PamFlag};
-/// use std::ffi::CStr;
-///
-/// # fn main() {}
-/// struct MyPamModule;
-/// pam_hooks!(MyPamModule);
-///
-/// impl PamHooks for MyPamModule {
-///    fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-///        println!("Everybody is authenticated!");
-///        PamResultCode::PAM_SUCCESS
-///    }
-///
-///    fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-///        println!("Everybody is authorized!");
-///        PamResultCode::PAM_SUCCESS
-///    }
-/// }
-/// ```
-#[macro_export]
-macro_rules! pam_hooks {
-    ($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()
-            }
-
-            #[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: &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_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_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_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)
-            }
-        }
-    };
-}
-
-#[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;
-
-    struct Foo;
-    impl PamHooks for Foo {}
-
-    pam_hooks!(Foo);
-}
--- a/pam/src/module.rs	Sat Mar 08 19:29:46 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,302 +0,0 @@
-//! Functions for use in pam modules.
-
-use libc::c_char;
-use std::ffi::{CStr, CString};
-
-use constants::{PamFlag, PamResultCode};
-use items::ItemType;
-
-/// 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.
-#[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 libc::c_void,
-    ) -> 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: ItemType,
-        item: &mut *const libc::c_void,
-    ) -> PamResultCode;
-
-    fn pam_set_item(
-        pamh: *mut PamHandle,
-        item_type: 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_authtok(
-        pamh: *const PamHandle,
-        item_type: ItemType,
-        data: &*mut c_char,
-        prompt: *const c_char,
-    ) -> PamResultCode;
-
-}
-
-pub extern "C" fn cleanup<T>(_: *const PamHandle, c_data: *mut libc::c_void, _: PamResultCode) {
-    unsafe {
-        let _data: Box<T> = Box::from_raw(c_data.cast::<T>());
-    }
-}
-
-pub type PamResult<T> = Result<T, PamResultCode>;
-
-impl PamHandle {
-    /// Gets some value, identified by `key`, that has been set by the module
-    /// previously.
-    ///
-    /// See the [`pam_get_data` manual page](
-    /// https://www.man7.org/linux/man-pages/man3/pam_get_data.3.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 function is undefined.
-    pub unsafe fn get_data<T>(&self, key: &str) -> PamResult<&T> {
-        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 = ptr.cast::<T>();
-            let data: &T = &*typed_ptr;
-            Ok(data)
-        } else {
-            Err(res)
-        }
-    }
-
-    /// Stores a value that can be retrieved later with `get_data`.  The value lives
-    /// as long as the current pam cycle.
-    ///
-    /// See the [`pam_set_data` manual page](
-    /// https://www.man7.org/linux/man-pages/man3/pam_set_data.3.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();
-        let res = unsafe {
-            pam_set_data(
-                self,
-                c_key.as_ptr(),
-                Box::into_raw(data).cast::<libc::c_void>(),
-                cleanup::<T>,
-            )
-        };
-        to_result(res)
-    }
-
-    /// Retrieves a value that has been set, possibly by the pam client.  This is
-    /// particularly useful for getting a `PamConv` reference.
-    ///
-    /// See the [`pam_get_item` manual page](
-    /// https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html).
-    ///
-    /// # 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::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)
-        };
-        match res {
-            PamResultCode::PAM_SUCCESS => Ok(item),
-            other => Err(other),
-        }
-    }
-
-    /// Sets a value in the pam context. The value can be retrieved using
-    /// `get_item`.
-    ///
-    /// Note that all items are strings, except `PAM_CONV` and `PAM_FAIL_DELAY`.
-    ///
-    /// See the [`pam_set_item` manual page](
-    /// https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html).
-    ///
-    /// # 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>()) };
-        to_result(res)
-    }
-
-    /// Retrieves the name of the user who is authenticating or logging in.
-    ///
-    /// This is really a specialization of `get_item`.
-    ///
-    /// See the [`pam_get_user` manual page](
-    /// https://www.man7.org/linux/man-pages/man3/pam_get_user.3.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 prompt_string;
-        let c_prompt = match prompt {
-            Some(p) => {
-                prompt_string = CString::new(p).unwrap();
-                prompt_string.as_ptr()
-            }
-            None => std::ptr::null(),
-        };
-        let output: *mut c_char = std::ptr::null_mut();
-        let res = unsafe { pam_get_user(self, &output, c_prompt) };
-        match res {
-            PamResultCode::PAM_SUCCESS => copy_pam_string(output),
-            otherwise => Err(otherwise),
-        }
-    }
-
-    /// Retrieves the authentication token from the user.
-    ///
-    /// This is really a specialization of `get_item`.
-    ///
-    /// See the [`pam_get_authtok` manual page](
-    /// https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.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_authtok(&self, prompt: Option<&str>) -> PamResult<String> {
-        let prompt_string;
-        let c_prompt = match prompt {
-            Some(p) => {
-                prompt_string = CString::new(p).unwrap();
-                prompt_string.as_ptr()
-            }
-            None => std::ptr::null(),
-        };
-        let output: *mut c_char = std::ptr::null_mut();
-        let res = unsafe { pam_get_authtok(self, ItemType::AuthTok, &output, c_prompt) };
-        match res {
-            PamResultCode::PAM_SUCCESS => copy_pam_string(output),
-            otherwise => Err(otherwise),
-        }
-    }
-}
-
-/// Creates an owned copy of a string that is returned from a
-/// <code>pam_get_<var>whatever</var></code> function.
-fn copy_pam_string(result_ptr: *const c_char) -> PamResult<String> {
-    // We really shouldn't get a null pointer back here, but if we do, return nothing.
-    if result_ptr.is_null() {
-        return Ok(String::from(""));
-    }
-    let bytes = unsafe { CStr::from_ptr(result_ptr).to_bytes() };
-    String::from_utf8(bytes.to_vec()).map_err(|_| PamResultCode::PAM_CONV_ERR)
-}
-
-/// Convenience to transform a `PamResultCode` into a unit `PamResult`.
-fn to_result(result: PamResultCode) -> PamResult<()> {
-    match result {
-        PamResultCode::PAM_SUCCESS => Ok(()),
-        otherwise => Err(otherwise),
-    }
-}
-
-/// Provides functions that are invoked by the entrypoints generated by the
-/// [`pam_hooks!` macro](../macro.pam_hooks.html).
-///
-/// All 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)`.
-#[allow(unused_variables)]
-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: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-        PamResultCode::PAM_IGNORE
-    }
-
-    /// This function performs the task of authenticating the user.
-    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: &mut 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: &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: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
-        PamResultCode::PAM_IGNORE
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/constants.rs	Tue Apr 15 00:50:23 2025 -0400
@@ -0,0 +1,66 @@
+use libc::{c_int, c_uint};
+
+// TODO: Import constants from C header file at compile time.
+
+pub type PamFlag = c_uint;
+pub type PamItemType = c_int;
+pub type PamMessageStyle = c_int;
+
+// The Linux-PAM flags
+// see /usr/include/security/_pam_types.h
+pub const PAM_SILENT: PamFlag = 0x8000;
+pub const PAM_DISALLOW_NULL_AUTHTOK: PamFlag = 0x0001;
+pub const PAM_ESTABLISH_CRED: PamFlag = 0x0002;
+pub const PAM_DELETE_CRED: PamFlag = 0x0004;
+pub const PAM_REINITIALIZE_CRED: PamFlag = 0x0008;
+pub const PAM_REFRESH_CRED: PamFlag = 0x0010;
+pub const PAM_CHANGE_EXPIRED_AUTHTOK: PamFlag = 0x0020;
+
+// Message styles
+pub const PAM_PROMPT_ECHO_OFF: PamMessageStyle = 1;
+pub const PAM_PROMPT_ECHO_ON: PamMessageStyle = 2;
+pub const PAM_ERROR_MSG: PamMessageStyle = 3;
+pub const PAM_TEXT_INFO: PamMessageStyle = 4;
+/// yes/no/maybe conditionals
+pub const PAM_RADIO_TYPE: PamMessageStyle = 5;
+pub const PAM_BINARY_PROMPT: PamMessageStyle = 7;
+
+// The Linux-PAM return values
+// see /usr/include/security/_pam_types.h
+#[allow(non_camel_case_types, dead_code)]
+#[derive(Debug, PartialEq)]
+#[repr(C)]
+pub enum PamResultCode {
+    PAM_SUCCESS = 0,
+    PAM_OPEN_ERR = 1,
+    PAM_SYMBOL_ERR = 2,
+    PAM_SERVICE_ERR = 3,
+    PAM_SYSTEM_ERR = 4,
+    PAM_BUF_ERR = 5,
+    PAM_PERM_DENIED = 6,
+    PAM_AUTH_ERR = 7,
+    PAM_CRED_INSUFFICIENT = 8,
+    PAM_AUTHINFO_UNAVAIL = 9,
+    PAM_USER_UNKNOWN = 10,
+    PAM_MAXTRIES = 11,
+    PAM_NEW_AUTHTOK_REQD = 12,
+    PAM_ACCT_EXPIRED = 13,
+    PAM_SESSION_ERR = 14,
+    PAM_CRED_UNAVAIL = 15,
+    PAM_CRED_EXPIRED = 16,
+    PAM_CRED_ERR = 17,
+    PAM_NO_MODULE_DATA = 18,
+    PAM_CONV_ERR = 19,
+    PAM_AUTHTOK_ERR = 20,
+    PAM_AUTHTOK_RECOVERY_ERR = 21,
+    PAM_AUTHTOK_LOCK_BUSY = 22,
+    PAM_AUTHTOK_DISABLE_AGING = 23,
+    PAM_TRY_AGAIN = 24,
+    PAM_IGNORE = 25,
+    PAM_ABORT = 26,
+    PAM_AUTHTOK_EXPIRED = 27,
+    PAM_MODULE_UNKNOWN = 28,
+    PAM_BAD_ITEM = 29,
+    PAM_CONV_AGAIN = 30,
+    PAM_INCOMPLETE = 31,
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/conv.rs	Tue Apr 15 00:50:23 2025 -0400
@@ -0,0 +1,94 @@
+use libc::{c_char, c_int};
+use std::ffi::{CStr, CString};
+use std::ptr;
+
+use constants::PamMessageStyle;
+use constants::PamResultCode;
+use items::Item;
+use module::PamResult;
+
+#[repr(C)]
+struct PamMessage {
+    msg_style: PamMessageStyle,
+    msg: *const c_char,
+}
+
+#[repr(C)]
+struct PamResponse {
+    resp: *const c_char,
+    resp_retcode: libc::c_int, // Unused - always zero
+}
+
+/// `PamConv` acts as a channel for communicating with user.
+///
+/// Communication is mediated by the pam client (the application that invoked
+/// pam).  Messages sent will be relayed to the user by the client, and response
+/// will be relayed back.
+#[repr(C)]
+pub struct Inner {
+    conv: extern "C" fn(
+        num_msg: c_int,
+        pam_message: &&PamMessage,
+        pam_response: &mut *const PamResponse,
+        appdata_ptr: *const libc::c_void,
+    ) -> PamResultCode,
+    appdata_ptr: *const libc::c_void,
+}
+
+pub struct Conv<'a>(&'a Inner);
+
+impl Conv<'_> {
+    /// Sends a message to the pam client.
+    ///
+    /// This will typically result in the user seeing a message or a prompt.
+    /// There are several message styles available:
+    ///
+    /// - PAM_PROMPT_ECHO_OFF
+    /// - PAM_PROMPT_ECHO_ON
+    /// - PAM_ERROR_MSG
+    /// - PAM_TEXT_INFO
+    /// - PAM_RADIO_TYPE
+    /// - PAM_BINARY_PROMPT
+    ///
+    /// 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<&CStr>> {
+        let mut resp_ptr: *const PamResponse = ptr::null();
+        let msg_cstr = CString::new(msg).unwrap();
+        let msg = PamMessage {
+            msg_style: style,
+            msg: msg_cstr.as_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
+            let response = unsafe { (*resp_ptr).resp };
+            if response.is_null() {
+                Ok(None)
+            } else {
+                Ok(Some(unsafe { CStr::from_ptr(response) }))
+            }
+        } else {
+            Err(ret)
+        }
+    }
+}
+
+impl Item for Conv<'_> {
+    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 _
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/items.rs	Tue Apr 15 00:50:23 2025 -0400
@@ -0,0 +1,88 @@
+#[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,
+}
+
+// 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;
+
+    /// 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;
+
+    /// The function to convert from this wrapper type to a C-compatible pointer.
+    fn into_raw(self) -> *const Self::Raw;
+}
+
+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<'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()
+            }
+        }
+    };
+}
+
+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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib.rs	Tue Apr 15 00:50:23 2025 -0400
@@ -0,0 +1,34 @@
+//! Interface to the pluggable authentication module framework (PAM).
+//!
+//! The goal of this library is to provide a type-safe API that can be used to
+//! interact with PAM.  The library is incomplete - currently it supports
+//! a subset of functions for use in a pam authentication module.  A pam module
+//! is a shared library that is invoked to authenticate a user, or to perform
+//! other functions.
+//!
+//! For general information on writing pam modules, see
+//! [The Linux-PAM Module Writers' Guide][module-guide]
+//!
+//! [module-guide]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/Linux-PAM_MWG.html
+//!
+//! A typical authentication module will define an external function called
+//! `pam_sm_authenticate()`, which will use functions in this library to
+//! interrogate the program that requested authentication for more information,
+//! and to render a result.  For a working example that uses this library, see
+//! [toznyauth-pam][].
+//!
+//! [toznyauth-pam]: https://github.com/tozny/toznyauth-pam
+//!
+//! Note that constants that are normally read from pam header files are
+//! hard-coded in the `constants` module.  The values there are taken from
+//! a Linux system.  That means that it might take some work to get this library
+//! to work on other platforms.
+
+extern crate libc;
+
+pub mod constants;
+pub mod conv;
+pub mod items;
+#[doc(hidden)]
+pub mod macros;
+pub mod module;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/macros.rs	Tue Apr 15 00:50:23 2025 -0400
@@ -0,0 +1,141 @@
+/// Macro to generate the `extern "C"` entrypoint bindings needed by PAM
+///
+/// You can call `pam_hooks!(SomeType);` for any type that implements `PamHooks`
+///
+/// ## Examples:
+///
+/// Here is full example of a PAM module that would authenticate and authorize everybody:
+///
+/// ```
+/// #[macro_use] extern crate pam;
+///
+/// use pam::module::{PamHooks, PamHandle};
+/// use pam::constants::{PamResultCode, PamFlag};
+/// use std::ffi::CStr;
+///
+/// # fn main() {}
+/// struct MyPamModule;
+/// pam_hooks!(MyPamModule);
+///
+/// impl PamHooks for MyPamModule {
+///    fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+///        println!("Everybody is authenticated!");
+///        PamResultCode::PAM_SUCCESS
+///    }
+///
+///    fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+///        println!("Everybody is authorized!");
+///        PamResultCode::PAM_SUCCESS
+///    }
+/// }
+/// ```
+#[macro_export]
+macro_rules! pam_hooks {
+    ($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()
+            }
+
+            #[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: &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_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_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_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)
+            }
+        }
+    };
+}
+
+#[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;
+
+    struct Foo;
+    impl PamHooks for Foo {}
+
+    pam_hooks!(Foo);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/module.rs	Tue Apr 15 00:50:23 2025 -0400
@@ -0,0 +1,303 @@
+//! Functions for use in pam modules.
+
+use libc::c_char;
+use std::ffi::{CStr, CString};
+
+use constants::{PamFlag, PamResultCode};
+use items::ItemType;
+
+/// 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.
+#[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 libc::c_void,
+    ) -> 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: ItemType,
+        item: &mut *const libc::c_void,
+    ) -> PamResultCode;
+
+    fn pam_set_item(
+        pamh: *mut PamHandle,
+        item_type: 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_authtok(
+        pamh: *const PamHandle,
+        item_type: ItemType,
+        data: &*mut c_char,
+        prompt: *const c_char,
+    ) -> PamResultCode;
+
+}
+
+pub extern "C" fn cleanup<T>(_: *const PamHandle, c_data: *mut libc::c_void, _: PamResultCode) {
+    unsafe {
+        let _data: Box<T> = Box::from_raw(c_data.cast::<T>());
+    }
+}
+
+pub type PamResult<T> = Result<T, PamResultCode>;
+
+impl PamHandle {
+    /// Gets some value, identified by `key`, that has been set by the module
+    /// previously.
+    ///
+    /// See the [`pam_get_data` manual page](
+    /// https://www.man7.org/linux/man-pages/man3/pam_get_data.3.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 function is undefined.
+    pub unsafe fn get_data<T>(&self, key: &str) -> PamResult<&T> {
+        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 = ptr.cast::<T>();
+            let data: &T = &*typed_ptr;
+            Ok(data)
+        } else {
+            Err(res)
+        }
+    }
+
+    /// Stores a value that can be retrieved later with `get_data`.  The value lives
+    /// as long as the current pam cycle.
+    ///
+    /// See the [`pam_set_data` manual page](
+    /// https://www.man7.org/linux/man-pages/man3/pam_set_data.3.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();
+        let res = unsafe {
+            pam_set_data(
+                self,
+                c_key.as_ptr(),
+                Box::into_raw(data).cast::<libc::c_void>(),
+                cleanup::<T>,
+            )
+        };
+        to_result(res)
+    }
+
+    /// Retrieves a value that has been set, possibly by the pam client.  This is
+    /// particularly useful for getting a `PamConv` reference.
+    ///
+    /// See the [`pam_get_item` manual page](
+    /// https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html).
+    ///
+    /// # 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::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)
+        };
+        match res {
+            PamResultCode::PAM_SUCCESS => Ok(item),
+            other => Err(other),
+        }
+    }
+
+    /// Sets a value in the pam context. The value can be retrieved using
+    /// `get_item`.
+    ///
+    /// Note that all items are strings, except `PAM_CONV` and `PAM_FAIL_DELAY`.
+    ///
+    /// See the [`pam_set_item` manual page](
+    /// https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html).
+    ///
+    /// # 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>()) };
+        to_result(res)
+    }
+
+    /// Retrieves the name of the user who is authenticating or logging in.
+    ///
+    /// This is really a specialization of `get_item`.
+    ///
+    /// See the [`pam_get_user` manual page](
+    /// https://www.man7.org/linux/man-pages/man3/pam_get_user.3.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 prompt_string;
+        let c_prompt = match prompt {
+            Some(p) => {
+                prompt_string = CString::new(p).unwrap();
+                prompt_string.as_ptr()
+            }
+            None => std::ptr::null(),
+        };
+        let output: *mut c_char = std::ptr::null_mut();
+        let res = unsafe { pam_get_user(self, &output, c_prompt) };
+        match res {
+            PamResultCode::PAM_SUCCESS => copy_pam_string(output),
+            otherwise => Err(otherwise),
+        }
+    }
+
+    /// Retrieves the authentication token from the user.
+    ///
+    /// This is really a specialization of `get_item`.
+    ///
+    /// See the [`pam_get_authtok` manual page](
+    /// https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.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_authtok(&self, prompt: Option<&str>) -> PamResult<String> {
+        let prompt_string;
+        let c_prompt = match prompt {
+            Some(p) => {
+                prompt_string = CString::new(p).unwrap();
+                prompt_string.as_ptr()
+            }
+            None => std::ptr::null(),
+        };
+        let output: *mut c_char = std::ptr::null_mut();
+        let res = unsafe { pam_get_authtok(self, ItemType::AuthTok, &output, c_prompt) };
+        match res {
+            PamResultCode::PAM_SUCCESS => copy_pam_string(output),
+            otherwise => Err(otherwise),
+        }
+    }
+}
+
+/// Creates an owned copy of a string that is returned from a
+/// <code>pam_get_<var>whatever</var></code> function.
+fn copy_pam_string(result_ptr: *const c_char) -> PamResult<String> {
+    // We really shouldn't get a null pointer back here, but if we do, return nothing.
+    if result_ptr.is_null() {
+        return Ok(String::from(""));
+    }
+    let bytes = unsafe { CStr::from_ptr(result_ptr).to_bytes() };
+    String::from_utf8(bytes.to_vec()).map_err(|_| PamResultCode::PAM_CONV_ERR)
+}
+
+/// Convenience to transform a `PamResultCode` into a unit `PamResult`.
+fn to_result(result: PamResultCode) -> PamResult<()> {
+    match result {
+        PamResultCode::PAM_SUCCESS => Ok(()),
+        otherwise => Err(otherwise),
+    }
+}
+
+/// Provides functions that are invoked by the entrypoints generated by the
+/// [`pam_hooks!` macro](../macro.pam_hooks.html).
+///
+/// All 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 [PAM’s root manual page](
+/// https://www.man7.org/linux/man-pages/man3/pam.3.html).
+#[allow(unused_variables)]
+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: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+        PamResultCode::PAM_IGNORE
+    }
+
+    /// This function performs the task of authenticating the user.
+    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: &mut 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: &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: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
+        PamResultCode::PAM_IGNORE
+    }
+}