Fix error handling and add nixos test

This commit is contained in:
Tim Schubert 2024-12-29 13:38:29 +01:00
parent c06b3a7142
commit ef60878471
Signed by: dadada
SSH key fingerprint: SHA256:bFAjFH3hR8zRBaJjzQDjc3o4jqoq5EZ87l+KXEjxIz0
6 changed files with 335 additions and 20 deletions

244
Cargo.lock generated
View file

@ -2,12 +2,65 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
[[package]]
name = "hostname"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba"
dependencies = [
"cfg-if",
"libc",
"windows",
]
[[package]]
name = "itoa"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "libc"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]]
name = "pam-bindings"
version = "0.1.1"
@ -21,5 +74,196 @@ dependencies = [
name = "pam-honeypot"
version = "0.1.0"
dependencies = [
"log",
"pam-bindings",
"syslog",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "2.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syslog"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "019f1500a13379b7d051455df397c75770de6311a7a188a699499502704d9f10"
dependencies = [
"hostname",
"libc",
"log",
"time",
]
[[package]]
name = "time"
version = "0.3.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
dependencies = [
"deranged",
"itoa",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "unicode-ident"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "windows"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
dependencies = [
"windows-core",
"windows-targets",
]
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View file

@ -9,3 +9,5 @@ crate-type = ["cdylib"]
[dependencies]
pam-bindings = { version = "0.1.1" }
log = "0"
syslog = "7.0.0"

View file

@ -2,7 +2,7 @@
description = "a653rs-router";
inputs = {
flake-utils.url = "github:numtide/flake-utils";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
@ -16,9 +16,9 @@
let
pkgs = import nixpkgs { inherit system; };
in
{
rec {
devShells.default = pkgs.mkShell {
nativeBuildInputs = [
nativeBuildInputs = [
pkgs.cargo
pkgs.clang
pkgs.clippy
@ -31,10 +31,25 @@
];
};
packages = {
default = pkgs.callPackage ./default.nix {};
default = pkgs.callPackage ./default.nix { };
};
checks = {
vm = nixpkgs.lib.nixos.runTest (
import ./test.nix {
pkgs = import nixpkgs {
inherit system;
overlays = [
(final: prev: {
pam_honeypot = packages.default;
})
];
};
}
);
};
}
) // {
)
// {
nixosModules = {
default = ./module.nix;
};

View file

@ -1,6 +1,13 @@
{ pkgs, ... }:
{ config, pkgs, ... }:
{
environment.etc."pam.d/pam_honeypot".text = ''
auth optional ${pkgs.pam_honeypot}/lib/pam_honeypot.so user=Admin password=AdminPwdQ1
'';
security.pam.services.login.rules.auth.pam_honeypot = {
enable = true;
order = config.security.pam.services.login.rules.auth.unix.order - 10;
control = "requisite";
modulePath = "${pkgs.pam_honeypot}/lib/libpam_honeypot.so";
args = [
"user=Admin"
"password=AdminPwdQ1"
];
};
}

View file

@ -1,11 +1,14 @@
use std::{collections::HashMap, env::temp_dir, ffi::CStr, fs::File};
use std::{collections::HashMap, ffi::CStr};
use log::{debug, info, trace, warn};
use pam::{
constants::{PamResultCode, PAM_PROMPT_ECHO_ON},
constants::{PamResultCode, PAM_PROMPT_ECHO_OFF},
conv::Conv,
items::User,
module::PamHooks,
pam_try,
};
use syslog::Facility;
struct PamHoneypot;
@ -15,12 +18,23 @@ impl PamHooks for PamHoneypot {
args: Vec<&CStr>,
_flags: pam::constants::PamFlag,
) -> pam::constants::PamResultCode {
let _ = syslog::init(
Facility::LOG_USER,
log::LevelFilter::Info,
Some("pam_honeypot"),
);
trace!("Running pam_honeypot");
let args: Vec<_> = args.iter().map(|s| s.to_string_lossy()).collect();
debug!("args = {args:?}");
let args: HashMap<&str, &str> = args
.iter()
.map(|s| {
let mut parts = s.splitn(2, '=');
(parts.next().unwrap(), parts.next().unwrap_or(""))
(
parts.next().expect("Failed to parse arguments"),
parts.next().unwrap_or(""),
)
})
.collect();
@ -28,13 +42,16 @@ impl PamHooks for PamHoneypot {
Some(user) => user,
_ => return PamResultCode::PAM_IGNORE,
};
debug!("honeypot user = {honey_user}");
let honey_password = match args.get("password") {
Some(password) => password,
_ => return PamResultCode::PAM_IGNORE,
};
let user = pam_try!(pamh.get_user(None));
if user != *honey_user {
trace!("Getting user from PAM");
let user: User = pam_try!(pamh.get_item()).unwrap_or(User(c""));
info!("Checking if entered username is honeypot user");
if user.to_string_lossy() != *honey_user {
return PamResultCode::PAM_IGNORE;
}
@ -47,20 +64,21 @@ impl PamHooks for PamHoneypot {
return err;
}
};
let password = pam_try!(conv.send(PAM_PROMPT_ECHO_ON, "Password"));
trace!("Getting password from PAM");
let password = pam_try!(conv.send(PAM_PROMPT_ECHO_OFF, "Password: "));
let password = match password {
Some(password) => Some(pam_try!(password.to_str(), PamResultCode::PAM_AUTH_ERR)),
_ => None,
};
if let Some(password) = password {
trace!("Checking if password is honeypot password");
if *honey_password == password {
let mut tmp = temp_dir();
tmp.push("honeypot");
let _ = File::create(tmp);
return PamResultCode::PAM_AUTH_ERR;
warn!("Someone tried to log in with the honeypot credentials");
} else {
info!("Entered username is not the honeypot user")
}
return PamResultCode::PAM_AUTH_ERR;
}
PamResultCode::PAM_IGNORE

29
test.nix Normal file
View file

@ -0,0 +1,29 @@
{
pkgs,
...
}:
{
name = "pam_honeypot-test";
hostPkgs = pkgs;
node.pkgs = pkgs;
nodes.machine =
{ ... }:
{
imports = [ ./module.nix ];
};
testScript = ''
machine.wait_for_unit("multi-user.target")
machine.send_key("alt-f2")
machine.wait_until_succeeds("[ $(fgconsole) = 2 ]")
machine.wait_for_unit("getty@tty2.service")
machine.wait_until_succeeds("pgrep -f 'agetty.*tty2'")
machine.wait_until_tty_matches("2", "login: ")
machine.send_chars("Admin\n")
machine.wait_until_tty_matches("2", "login: Admin")
machine.wait_until_succeeds("pgrep login")
machine.wait_until_tty_matches("2", "Password: ")
machine.send_chars("AdminPwdQ1\n")
machine.wait_until_tty_matches("2", "login: ")
machine.succeed("journalctl | grep 'log in with the honeypot credentials'")
'';
}