initial commit

This commit is contained in:
Tyler Murphy 2022-11-08 19:35:01 -05:00
commit c98b78f4c1
7 changed files with 556 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
Cargo.lock

326
Cargo.lock generated Normal file
View file

@ -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"

14
Cargo.toml Normal file
View file

@ -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"

21
build.sh Executable file
View file

@ -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

1
conf Normal file
View file

@ -0,0 +1 @@
root true

4
pam Normal file
View file

@ -0,0 +1,4 @@
#%PAM-1.0
auth include system-auth
account include system-auth
session include system-auth

188
src/main.rs Normal file
View file

@ -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);
}