initial commit
This commit is contained in:
commit
c98b78f4c1
7 changed files with 556 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
Cargo.lock
|
326
Cargo.lock
generated
Normal file
326
Cargo.lock
generated
Normal 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
14
Cargo.toml
Normal 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
21
build.sh
Executable 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
1
conf
Normal file
|
@ -0,0 +1 @@
|
|||
root true
|
4
pam
Normal file
4
pam
Normal 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
188
src/main.rs
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue