From e35b14a92bd382927f30b812a7db98330225b6d0 Mon Sep 17 00:00:00 2001 From: Tim Schubert Date: Sun, 29 Dec 2024 13:38:29 +0100 Subject: [PATCH] Fix error handling and add nixos test --- .nixos-test-history | 78 ++++++++++++++ Cargo.lock | 244 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + flake.nix | 25 ++++- module.nix | 15 ++- result | 1 + src/lib.rs | 40 ++++++-- test.nix | 29 ++++++ 8 files changed, 414 insertions(+), 20 deletions(-) create mode 100644 .nixos-test-history create mode 120000 result create mode 100644 test.nix diff --git a/.nixos-test-history b/.nixos-test-history new file mode 100644 index 0000000..0985c05 --- /dev/null +++ b/.nixos-test-history @@ -0,0 +1,78 @@ + +# 2024-12-29 13:58:54.893983 ++machine.shell_interact() + +# 2024-12-29 14:04:50.006813 ++machine.wait_for_unit("default.target") + +# 2024-12-29 14:05:20.701959 ++machine.send_chars("Admin\n") + +# 2024-12-29 14:05:34.563727 ++machine.send_chars("APwdQ1dmin\n") + +# 2024-12-29 14:06:02.762977 ++machine.send_chars("Admin") + +# 2024-12-29 14:06:16.344513 ++machine.send_chars("APwdQ1dmin\n") + +# 2024-12-29 14:07:32.702436 ++machine.send_chars(b"Admin\n") + +# 2024-12-29 14:07:39.866524 ++machine.send_chars(b"Admin") + +# 2024-12-29 14:07:42.979641 ++machine.send_chars("Admin") + +# 2024-12-29 14:07:48.519884 ++machine.send_chars("Admin\n") + +# 2024-12-29 14:08:14.028807 ++machine.send_chars("Admin") + +# 2024-12-29 14:08:22.651984 ++machine.send_chars("Admin\n") + +# 2024-12-29 14:08:44.603595 ++machsend_chars("Admin\n") + +# 2024-12-29 14:08:54.793100 ++machine.send_chars("Admin\n") + +# 2024-12-29 14:09:15.589406 ++machine.send_chars("AdminPwdQ1\n") + +# 2024-12-29 15:32:44.284835 ++machine.send_chars("Admin\r") + +# 2024-12-29 15:32:51.799888 ++machine.execute() + +# 2024-12-29 15:33:01.065792 ++machine.connect + +# 2024-12-29 15:33:04.371022 ++machine.connect() + +# 2024-12-29 15:34:01.471220 ++machine.send_chars("Admin\r") + +# 2024-12-29 15:34:08.785393 ++machine.send_chars("Admin\x0D") + +# 2024-12-29 15:34:17.570504 ++machine.send_chars("Admin\lf") + +# 2024-12-29 15:34:20.671653 ++machine.send_chars("Admin\l") + +# 2024-12-29 15:35:12.675488 ++machine.send_chars("Admin\n") + +# 2024-12-29 15:35:47.369820 ++machine.send_chars("AdminPwdQ1\n") + +# 2024-12-29 15:36:11.188925 ++machine.send_chars("Admin\n") diff --git a/Cargo.lock b/Cargo.lock index 8ee6cc2..508b2f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index f303a78..9fc44c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,5 @@ crate-type = ["cdylib"] [dependencies] pam-bindings = { version = "0.1.1" } +log = "0" +syslog = "7.0.0" diff --git a/flake.nix b/flake.nix index c4cb499..9fcb8f6 100644 --- a/flake.nix +++ b/flake.nix @@ -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; }; diff --git a/module.nix b/module.nix index 744e664..4e4c7dd 100644 --- a/module.nix +++ b/module.nix @@ -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" + ]; + }; } diff --git a/result b/result new file mode 120000 index 0000000..050d789 --- /dev/null +++ b/result @@ -0,0 +1 @@ +/nix/store/07wmlfzaddkcci6a67kvj0k7slcn2kwm-vm-test-run-pam_honeypot-test \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index dc03e9e..627e73e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 diff --git a/test.nix b/test.nix new file mode 100644 index 0000000..39c162b --- /dev/null +++ b/test.nix @@ -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'") + ''; +}