summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Murphy <tylermurphy534@gmail.com>2022-11-08 19:35:01 -0500
committerTyler Murphy <tylermurphy534@gmail.com>2022-11-08 19:35:01 -0500
commitc98b78f4c17a0d997de2cf7aef1f8a22e1a92616 (patch)
tree4b4146cc21cd47e09f5d54d57bb09567b3dac99d
downloadcrab-c98b78f4c17a0d997de2cf7aef1f8a22e1a92616.tar.gz
crab-c98b78f4c17a0d997de2cf7aef1f8a22e1a92616.tar.bz2
crab-c98b78f4c17a0d997de2cf7aef1f8a22e1a92616.zip
initial commit
-rw-r--r--.gitignore2
-rw-r--r--Cargo.lock326
-rw-r--r--Cargo.toml14
-rwxr-xr-xbuild.sh21
-rw-r--r--conf1
-rw-r--r--pam4
-rw-r--r--src/main.rs188
7 files changed, 556 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96ef6c0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+Cargo.lock
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..e401bd5
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,326 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "cc"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41ca34107f97baef6cfb231b32f36115781856b8f8208e8c580e0bcaea374842"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "crab"
+version = "4.2.0"
+dependencies = [
+ "exec",
+ "nix",
+ "pam",
+ "procinfo",
+ "pwd",
+ "rpassword",
+ "serde_json",
+ "time",
+]
+
+[[package]]
+name = "errno"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "exec"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "886b70328cba8871bfc025858e1de4be16b1d5088f2ba50b57816f4210672615"
+dependencies = [
+ "errno",
+ "libc",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+
+[[package]]
+name = "libc"
+version = "0.2.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "nix"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
+dependencies = [
+ "autocfg",
+ "bitflags",
+ "cfg-if",
+ "libc",
+ "memoffset",
+ "pin-utils",
+]
+
+[[package]]
+name = "nom"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff"
+
+[[package]]
+name = "pam"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa2bdc959c201c047004a1420a92aaa1dd1a6b64d5ef333aa3a4ac764fb93097"
+dependencies = [
+ "libc",
+ "pam-sys",
+ "users",
+]
+
+[[package]]
+name = "pam-sys"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd4858311a097f01a0006ef7d0cd50bca81ec430c949d7bf95cbefd202282434"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "procinfo"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ab1427f3d2635891f842892dda177883dca0639e05fe66796a62c9d2f23b49c"
+dependencies = [
+ "byteorder",
+ "libc",
+ "nom",
+ "rustc_version",
+]
+
+[[package]]
+name = "pwd"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72c71c0c79b9701efe4e1e4b563b2016dd4ee789eb99badcb09d61ac4b92e4a2"
+dependencies = [
+ "libc",
+ "thiserror",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rpassword"
+version = "7.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20c9f5d2a0c3e2ea729ab3706d22217177770654c3ef5056b68b69d07332d3f5"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "serde"
+version = "1.0.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
+
+[[package]]
+name = "serde_json"
+version = "1.0.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "time"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
+dependencies = [
+ "serde",
+ "time-core",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+
+[[package]]
+name = "users"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fed7d0912567d35f88010c23dbaf865e9da8b5227295e8dc0f2fdd109155ab7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..d063d87
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "crab"
+version = "4.2.0"
+edition = "2021"
+
+[dependencies]
+pam = "0.7.0"
+pwd = "1.4.0"
+nix = "0.25.0"
+exec = "0.3.1"
+rpassword = "7.1.0"
+serde_json = "1.0.87"
+procinfo = "0.4.2"
+time = "0.3.17" \ No newline at end of file
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..b9e3c57
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+if [[ $(/usr/bin/id -u) -ne 0 ]]; then
+ echo "Please run this as root"
+ exit
+fi
+
+# Make sure rust is able to build as root
+rustup default stable
+
+# Build crab
+cargo build --release
+
+# Copy executable to bin
+cp ./target/release/crab /bin/crab
+chown root:root /bin/crab
+chmod 6755 /bin/crab
+
+# Set up config files
+cp pam /etc/pam.d/crab
+cp -n conf /etc/crab.conf \ No newline at end of file
diff --git a/conf b/conf
new file mode 100644
index 0000000..bebb505
--- /dev/null
+++ b/conf
@@ -0,0 +1 @@
+root true \ No newline at end of file
diff --git a/pam b/pam
new file mode 100644
index 0000000..b98216b
--- /dev/null
+++ b/pam
@@ -0,0 +1,4 @@
+#%PAM-1.0
+auth include system-auth
+account include system-auth
+session include system-auth
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..dd8eeb3
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,188 @@
+use std::fs;
+use std::{env, os::unix::prelude::PermissionsExt};
+use std::process::ExitCode;
+use std::time::SystemTime;
+use pwd::Passwd;
+use nix::{unistd};
+use serde_json::Value;
+
+extern crate time;
+
+fn main() -> ExitCode {
+ let args: Vec<String> = env::args().collect();
+ if args.len() < 2 {
+ eprintln!("Invalid argument count.");
+ return ExitCode::from(0);
+ }
+ let config = match config("/etc/crab.conf") {
+ Some(data) => data,
+ None => return ExitCode::from(1)
+ };
+ let user = match Passwd::current_user() {
+ Some(data) => data,
+ None => {
+ eprintln!("You dont exist.");
+ return ExitCode::from(2);
+ }
+ };
+ let persist = match allowed(&config, &user.name) {
+ Some(data) => data,
+ None => {
+ eprintln!("Operation Not Permitted. This incidence will be reported.");
+ return ExitCode::from(3);
+ }
+ };
+
+ if !validate(&user.name, persist) {
+ eprintln!("Authentication failed.");
+ return ExitCode::from(4);
+ }
+
+ if !unistd::setuid(unistd::geteuid()).is_ok() || !unistd::setgid(unistd::getegid()).is_ok() {
+ eprintln!("Failed to set root permissions");
+ return ExitCode::from(5);
+ };
+
+ let err = exec::execvp(&args[1], &args[1..]);
+ println!("Error: {}", err);
+
+ ExitCode::from(0)
+}
+
+struct Config {
+ users: Vec<(String, bool)>
+}
+
+fn validate(user: &str, persist: bool) -> bool {
+ if persist && get_persist(user) {
+ return true;
+ }
+ let input = rpassword::prompt_password(format!("crab ({}) password: ", user)).unwrap();
+ let mut auth = pam::Authenticator::with_password("crab").unwrap();
+ auth.get_handler().set_credentials(user.to_owned(), input);
+ if !auth.authenticate().is_ok() || !auth.open_session().is_ok() {
+ return false;
+ }
+ if persist {
+ set_persist(user);
+ }
+ return true;
+}
+
+fn allowed(config: &Config, user: &str) -> Option<bool> {
+ for (name, persist) in &config.users {
+ if name == user {
+ return Some(persist.clone());
+ }
+ }
+ None
+}
+
+fn config(path: &str) -> Option<Config> {
+ let file = match std::fs::read_to_string(path) {
+ Err(e) => {
+ eprintln!("{}: {}", &path, e);
+ return None
+ },
+ Ok(data) => data
+ };
+
+ let mut users = vec![];
+ for (line_num, line) in file.split("\n").enumerate() {
+ let args: Vec<&str> = line.split(" ").collect();
+ if line.trim() == "" {
+ continue;
+ }
+ if args.len() < 2 {
+ eprintln!("Error in config at line {}: Not enough arguments", line_num);
+ continue;
+ }
+ let user: String = args[0].to_string();
+ let persist: bool = match args[1].parse() {
+ Err(e) => {
+ eprintln!("Error in config at line {}: {}", line_num, e);
+ continue;
+ },
+ Ok(data) => data
+ };
+ users.push((user, persist));
+ }
+ Some(Config{users})
+}
+
+fn get_terminal_process() -> Option<i32> {
+ let id: i32 = match std::process::id().try_into() {
+ Ok(data) => data,
+ Err(_) => return None
+ };
+ let stat = match procinfo::pid::stat(id) {
+ Ok(data) => data,
+ Err(_) => return None
+ };
+ Some(stat.tty_nr)
+}
+
+fn get_terminal_config() -> Option<Value> {
+ let id = match get_terminal_process() {
+ Some(data) => data,
+ None => return None
+ };
+ let data = match std::fs::read_to_string(path(&id)) {
+ Ok(data) => data,
+ Err(_) => "{}".to_string()
+ };
+ let json: Value = match serde_json::from_str(&data) {
+ Ok(data) => data,
+ Err(_) => return None
+ };
+ Some(json)
+}
+
+fn write_terminal_config(id: &i32, data: &str) -> Result<(), Box<dyn std::error::Error>> {
+ std::fs::write(path(&id), data)?;
+ unistd::chown(std::path::Path::new(&path(&id)), Some(unistd::Uid::from(0)), Some(unistd::Gid::from(0)))?;
+ let metadata = std::fs::metadata(path(&id))?;
+ let mut perms = metadata.permissions();
+ perms.set_mode(0o0660);
+ fs::set_permissions(path(&id), perms)?;
+ Ok(())
+}
+
+
+fn get_persist(user: &str) -> bool {
+ let json = match get_terminal_config() {
+ Some(data) => data,
+ None => return false
+ };
+ let timestamp = match json[user].as_u64() {
+ Some(data) => data,
+ None => return false
+ };
+ return now() - timestamp < 60 * 3;
+}
+
+fn set_persist(user: &str) {
+ let mut json = match get_terminal_config() {
+ Some(data) => data,
+ None => return
+ };
+ json[user] = Value::from(now());
+ let id = match get_terminal_process() {
+ Some(data) => data,
+ None => return
+ };
+ match write_terminal_config(&id, &json.to_string()) {
+ Ok(_) => {},
+ Err(e) => {
+ eprintln!("Internal Error: {}", e)
+ }
+ };
+}
+
+fn now() -> u64 {
+ return SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
+}
+
+fn path(id: &i32) -> String {
+ return format!("/tmp/crab-{}", id);
+} \ No newline at end of file