made rudo lol
commit
bf66312e8a
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,519 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fork"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf2ca97a59201425e7ee4d197c9c4fea282fe87a97d666a580bda889b95b8e88"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "pam"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa2bdc959c201c047004a1420a92aaa1dd1a6b64d5ef333aa3a4ac764fb93097"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pam-sys",
|
||||
"users 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pam-sys"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cce697055a5e48d8907841682cd14ccc01494dfb39453335941cca6dde524b39"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rpassword"
|
||||
version = "7.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rtoolbox",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtoolbox"
|
||||
version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rudo"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"clap",
|
||||
"fork",
|
||||
"libc",
|
||||
"pam",
|
||||
"rpassword",
|
||||
"serde",
|
||||
"toml",
|
||||
"unix_mode",
|
||||
"users 0.11.0",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.189"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.189"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unix_mode"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b55eedc365f81a3c32aea49baf23fa965e3cd85bcc28fb8045708c7388d124ef"
|
||||
|
||||
[[package]]
|
||||
name = "users"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fed7d0912567d35f88010c23dbaf865e9da8b5227295e8dc0f2fdd109155ab7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "users"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "rudo"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
clap = { version = "4.4.6", features = ["derive"] }
|
||||
fork = "0.1.22"
|
||||
libc = "0.2.149"
|
||||
pam = "0.7.0"
|
||||
rpassword = "7.2.0"
|
||||
serde = { version = "1.0.189", features = ["derive"] }
|
||||
toml = "0.8.2"
|
||||
unix_mode = "0.1.4"
|
||||
users = "0.11.0"
|
||||
which = "4.4.2"
|
|
@ -0,0 +1,53 @@
|
|||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None, disable_help_flag = true, disable_version_flag = true)]
|
||||
pub struct App {
|
||||
/// Run the given command in background mode. Caution: backgrounded processes are not subject to shell job control. Interactive commands may misbehave.
|
||||
#[arg(short, long)]
|
||||
pub background: bool,
|
||||
/// run as the specified user
|
||||
#[arg(short, long)]
|
||||
pub user: Option<String>,
|
||||
#[arg(short, long)]
|
||||
pub validate: bool,
|
||||
pub cmd: Option<Vec<String>>,
|
||||
// /// Run the command from the specified directory. The security policy may return an error if the user does not have permission to specify the working directory.
|
||||
// #[arg(short = 'D', long)]
|
||||
// pub chdir: Option<PathBuf>,
|
||||
// /// Indicates to the security policy that the user wishes to preserve existing environment variables. Subject to security policy.
|
||||
// #[arg(short, long)]
|
||||
// pub preserve_env: bool,
|
||||
// #[arg(long)]
|
||||
// help: bool,
|
||||
// /// Specify group via name or `#<group number>`
|
||||
// #[arg(short, long)]
|
||||
// pub group: Option<String>,
|
||||
// #[arg(short = 'H', long)]
|
||||
// pub set_home: bool,
|
||||
// #[arg(short = 'h', long)]
|
||||
// pub host: Option<String>,
|
||||
// #[arg(short = 'i', long)]
|
||||
// pub login: bool,
|
||||
// /// Remove all cached credentials for user.
|
||||
// #[arg(short = 'K', long)]
|
||||
// pub remove_timestamp: bool,
|
||||
// /// Remove current shell's cached credentials.
|
||||
// #[arg(short, long)]
|
||||
// pub reset_timestamp: bool,
|
||||
// /// Don't update cached credentials.
|
||||
// #[arg(short = 'N', long)]
|
||||
// pub no_update: bool,
|
||||
// /// List privileges for user.
|
||||
// #[arg(short, long)]
|
||||
// pub list: Option<String>,
|
||||
// #[arg(short = 'R', long)]
|
||||
// pub chroot: Option<PathBuf>,
|
||||
// /// Write prompt to stderr and read from stdin instead of terminal input.
|
||||
// #[arg(short = 'S', long)]
|
||||
// pub stdin: bool,
|
||||
// /// Run shell specified in `SHELL`.
|
||||
// #[arg(short, long)]
|
||||
// pub shell: bool,
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
use crate::{error::RudoError, user_info::get_username, AUTH_SERVICE, RETRIES};
|
||||
|
||||
pub fn check_auth() -> Result<(), RudoError> {
|
||||
let login = get_username().unwrap();
|
||||
let mut retries = RETRIES;
|
||||
for i in 0..retries {
|
||||
let password = rpassword::prompt_password(format!["password for {login}: "]).unwrap();
|
||||
let mut auth = pam::Authenticator::with_password(AUTH_SERVICE).unwrap();
|
||||
auth.get_handler().set_credentials(&login, password);
|
||||
match auth.authenticate() {
|
||||
Ok(()) => return Ok(()),
|
||||
Err(_) => {
|
||||
if i < retries - 1 {
|
||||
eprintln!["incorrect password, try again"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(RudoError::AuthenticationError)
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RudoError {
|
||||
AuthenticationError,
|
||||
NoCommandSpecified,
|
||||
CommandNotFound,
|
||||
PermissionDenied,
|
||||
MalformedRudoersToml,
|
||||
NotInRudoers,
|
||||
NoSwitchEntry,
|
||||
UnknownError,
|
||||
BadHostname,
|
||||
BadUsername,
|
||||
BadGroupname,
|
||||
}
|
||||
|
||||
impl Display for RudoError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let text: &str = match self {
|
||||
RudoError::AuthenticationError => "incorrect password".into(),
|
||||
RudoError::NoCommandSpecified => "no command specified".into(),
|
||||
RudoError::PermissionDenied => "permission denied :)".into(),
|
||||
RudoError::MalformedRudoersToml => "malformed rudoers TODO: display error span".into(),
|
||||
RudoError::NotInRudoers => "user is not in the rudoers file! >:C".into(),
|
||||
RudoError::NoSwitchEntry => "not allowed to run as that user! >:C".into(),
|
||||
RudoError::UnknownError => "unknown error".into(),
|
||||
RudoError::BadHostname => "bad hostname".into(),
|
||||
RudoError::BadUsername => "bad username".into(),
|
||||
RudoError::BadGroupname => "bad groupname".into(),
|
||||
RudoError::CommandNotFound => "command not found".into(),
|
||||
};
|
||||
writeln![f, "{text}"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
use std::{
|
||||
fs::{DirBuilder, File, OpenOptions},
|
||||
io::{Error, Read, Write},
|
||||
os::unix::process::CommandExt,
|
||||
path::PathBuf,
|
||||
process::Command,
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
mod app;
|
||||
use app::App;
|
||||
use clap::Parser;
|
||||
use error::RudoError;
|
||||
mod error;
|
||||
mod rudoers;
|
||||
mod user_info;
|
||||
use fork::{daemon, Fork};
|
||||
use libc::kill;
|
||||
use rudoers::check_rudoers;
|
||||
mod authentication;
|
||||
use authentication::check_auth;
|
||||
use user_info::get_command_path;
|
||||
|
||||
/// RUDOLPH THE RED NOSE REINDEER
|
||||
/// HAD A VERY SHINY NOSE
|
||||
/// AND IF YOU EVER SAW IT
|
||||
/// YOU MIGHT EVEN SAY IT GLOWS
|
||||
|
||||
const DEFAULT_RUDOERS: &'static str = "/etc/rudoers.toml";
|
||||
const AUTH_SERVICE: &'static str = "system-auth";
|
||||
const RETRIES: u32 = 3;
|
||||
const DEFAULT_AUTH_CACHE_DURATION: u32 = 15 * 60;
|
||||
const AUTH_CACHE_PATH: &str = "/var/db/rudo/cache/auth/";
|
||||
const SESSION_CACHE_PATH: &str = "/var/db/rudo/cache/session/";
|
||||
|
||||
pub fn update_auth_cache(alias: &str, ppid: &str) -> Result<(), std::io::Error> {
|
||||
let mut path = PathBuf::from(AUTH_CACHE_PATH);
|
||||
path.push(ppid);
|
||||
DirBuilder::new().recursive(true).create(&path)?;
|
||||
path.push(alias);
|
||||
let mut opts = OpenOptions::new();
|
||||
opts.write(true).create(true);
|
||||
let mut file = opts.open(&path)?;
|
||||
let time = Instant::now();
|
||||
let time = unsafe { std::mem::transmute::<Instant, u128>(time) };
|
||||
file.write(&bincode::serialize(&time).unwrap())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_auth_cache(alias: &str, ppid: &str) -> bool {
|
||||
let mut path = PathBuf::from(AUTH_CACHE_PATH);
|
||||
path.push(ppid);
|
||||
path.push(alias);
|
||||
let mut file = if let Ok(file) = File::open(&path) {
|
||||
file
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
if let Err(_) = file.read_to_end(&mut buf) {
|
||||
return false;
|
||||
};
|
||||
match bincode::deserialize::<u128>(&buf) {
|
||||
Ok(n) => {
|
||||
let old_time = unsafe { std::mem::transmute::<u128, Instant>(n) };
|
||||
if old_time.elapsed() < Duration::from_secs(DEFAULT_AUTH_CACHE_DURATION as u64) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
dbg!["{}", e];
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(app: &App) -> Result<(), RudoError> {
|
||||
if let Some(ref cmds) = app.cmd {
|
||||
let run_as_name = match &app.user {
|
||||
Some(x) => &x,
|
||||
None => "root",
|
||||
};
|
||||
let run_as = users::get_user_by_name(run_as_name).unwrap();
|
||||
|
||||
let mut child = Command::new(get_command_path(cmds.first().unwrap())?)
|
||||
.uid(run_as.uid())
|
||||
.args(&cmds[1..])
|
||||
.spawn()
|
||||
.unwrap();
|
||||
if app.background {
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
child.wait().unwrap();
|
||||
}
|
||||
} else {
|
||||
return Err(RudoError::NoCommandSpecified);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run() -> Result<(), RudoError> {
|
||||
let app = App::parse();
|
||||
let alias = match &app.user {
|
||||
Some(x) => &x,
|
||||
None => "root",
|
||||
};
|
||||
|
||||
let parent_id = std::os::unix::process::parent_id();
|
||||
let parent_id_str = format!["{parent_id}"];
|
||||
|
||||
let needs_password = check_rudoers(&app)? && !check_auth_cache(alias, &parent_id_str) || app.validate;
|
||||
if needs_password {
|
||||
check_auth()?;
|
||||
}
|
||||
|
||||
if let Err(e) = update_auth_cache(alias, &parent_id_str) {
|
||||
eprintln!["Failed to update cache: {e}"];
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let mut path = PathBuf::from(SESSION_CACHE_PATH);
|
||||
path.push(&parent_id_str);
|
||||
match OpenOptions::new().create_new(true).write(true).open(&path) {
|
||||
Ok(_) => {
|
||||
if let Ok(Fork::Child) = daemon(false, false) {
|
||||
loop {
|
||||
if unsafe { kill(parent_id as _, 0) } == -1 {
|
||||
if let Err(e) = std::fs::remove_file(&path) {
|
||||
println!["Failed to remove session file: {e}"];
|
||||
std::process::exit(1);
|
||||
}
|
||||
let mut path = PathBuf::from(AUTH_CACHE_PATH);
|
||||
path.push(parent_id_str);
|
||||
if let Err(e) = std::fs::remove_dir_all(&path) {
|
||||
println!["Failed to remove session dir: {e}"];
|
||||
std::process::exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(1000))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => (),
|
||||
};
|
||||
|
||||
// Run the command
|
||||
execute(&app)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
match run() {
|
||||
Ok(()) => (),
|
||||
Err(e) => eprintln!["{e}"],
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
use users::Group;
|
||||
|
||||
use crate::{app::App, user_info::*};
|
||||
use crate::{error::RudoError, DEFAULT_RUDOERS};
|
||||
|
||||
const ALL: &'static str = "ALL";
|
||||
|
||||
fn default_password_required() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct HostPerms {
|
||||
#[serde(default = "default_password_required")]
|
||||
password_required: bool,
|
||||
aliases: BTreeSet<String>,
|
||||
#[serde(default)]
|
||||
commands: BTreeSet<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Entry {
|
||||
CommandPaths(BTreeSet<String>),
|
||||
Hosts(BTreeMap<String, HostPerms>),
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub struct Rudoers {
|
||||
users: BTreeMap<String, Entry>,
|
||||
#[serde(default)]
|
||||
groups: BTreeMap<String, Entry>,
|
||||
}
|
||||
|
||||
fn parse_rudoers() -> Rudoers {
|
||||
let mut rudoers = match File::open(DEFAULT_RUDOERS) {
|
||||
Ok(file) => file,
|
||||
Err(e) => {
|
||||
eprintln!["Cannot open {DEFAULT_RUDOERS}: {e}"];
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let mut contents = String::new();
|
||||
match rudoers.read_to_string(&mut contents) {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
eprintln!["Could not read {DEFAULT_RUDOERS}: {e}"];
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
match toml::from_str(&contents) {
|
||||
Ok(rudoers) => rudoers,
|
||||
Err(e) => {
|
||||
eprintln!["Failed to parse {DEFAULT_RUDOERS}: {e}"];
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_entry(
|
||||
entry: &Entry,
|
||||
hostname: &str,
|
||||
alias: &str,
|
||||
command_path: &str,
|
||||
) -> Result<bool, RudoError> {
|
||||
match entry {
|
||||
Entry::CommandPaths(cmds) => {
|
||||
if cmds.contains(command_path) || cmds.contains(ALL) {
|
||||
Ok(true)
|
||||
} else {
|
||||
Err(RudoError::PermissionDenied)
|
||||
}
|
||||
}
|
||||
Entry::Hosts(hosts) => {
|
||||
if let Some(ref host) = hosts.get(hostname).or_else(|| hosts.get(ALL)) {
|
||||
if host.aliases.contains(alias) || host.aliases.contains(ALL) {
|
||||
if host.commands.contains(command_path) || host.commands.contains(ALL) {
|
||||
Ok(host.password_required)
|
||||
} else {
|
||||
Err(RudoError::PermissionDenied)
|
||||
}
|
||||
} else {
|
||||
Err(if alias == "root" {
|
||||
RudoError::PermissionDenied
|
||||
} else {
|
||||
RudoError::NoSwitchEntry
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Err(RudoError::PermissionDenied)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_rudoers(args: &App) -> Result<bool, RudoError> {
|
||||
let command_path: PathBuf = if let Some(ref cmd) = args.cmd {
|
||||
get_command_path(cmd.first().unwrap())?
|
||||
} else {
|
||||
return Err(RudoError::NoCommandSpecified);
|
||||
};
|
||||
let alias: String = args.user.clone().unwrap_or("root".into());
|
||||
let hostname: String = get_hostname()?;
|
||||
let username: String = get_username()?;
|
||||
let groups: Vec<Group> = get_groups();
|
||||
let rudoers = parse_rudoers();
|
||||
Ok(if let Some(entry) = rudoers.users.get(&username) {
|
||||
check_entry(&entry, &hostname, &alias, &command_path.to_string_lossy())?
|
||||
} else {
|
||||
let mut result = Err(RudoError::NotInRudoers);
|
||||
for group in groups {
|
||||
if let Some(entry) = rudoers.groups.get(group.name().to_str().unwrap()) {
|
||||
let output =
|
||||
check_entry(&entry, &hostname, &alias, &command_path.to_string_lossy());
|
||||
if output.is_ok() {
|
||||
result = output;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
result?
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rudoers_toml() {
|
||||
let mut rudoers = Rudoers::default();
|
||||
rudoers.users.insert(
|
||||
"alice".to_string(),
|
||||
Entry::Hosts({
|
||||
let mut hosts = BTreeMap::new();
|
||||
hosts.insert(
|
||||
"northpole".to_string(),
|
||||
HostPerms {
|
||||
password_required: false,
|
||||
aliases: {
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert("root".to_string());
|
||||
set
|
||||
},
|
||||
commands: {
|
||||
let mut commands = BTreeSet::new();
|
||||
commands.insert("/bin/cat".into());
|
||||
commands.insert("/bin/grep".into());
|
||||
commands.insert("/bin/xargs".into());
|
||||
commands
|
||||
},
|
||||
},
|
||||
);
|
||||
hosts
|
||||
}),
|
||||
);
|
||||
rudoers.users.insert(
|
||||
"bob".to_string(),
|
||||
Entry::Hosts({
|
||||
let mut hosts = BTreeMap::new();
|
||||
hosts.insert(
|
||||
"northpole".to_string(),
|
||||
HostPerms {
|
||||
password_required: false,
|
||||
aliases: {
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert("root".to_string());
|
||||
set
|
||||
},
|
||||
commands: {
|
||||
let mut commands = BTreeSet::new();
|
||||
commands.insert("/bin/cat".into());
|
||||
commands.insert("/bin/grep".into());
|
||||
commands.insert("/bin/xargs".into());
|
||||
commands
|
||||
},
|
||||
},
|
||||
);
|
||||
hosts
|
||||
}),
|
||||
);
|
||||
let rudoers = toml::to_string(&rudoers).unwrap();
|
||||
let mut text = String::new();
|
||||
for (n, s) in rudoers.lines().enumerate() {
|
||||
if n > 0 {
|
||||
text.push('\n');
|
||||
}
|
||||
if s.starts_with(|c: char| !c.is_whitespace()) {
|
||||
text.push_str(&format!["# {s}"]);
|
||||
} else {
|
||||
text.push_str(&s);
|
||||
}
|
||||
}
|
||||
text.push('\n');
|
||||
File::create("test_config")
|
||||
.unwrap()
|
||||
.write_all(&text.into_bytes())
|
||||
.unwrap();
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use libc::gethostname;
|
||||
use users::{get_current_gid, get_current_username, Group};
|
||||
use which::which;
|
||||
|
||||
use crate::error::RudoError;
|
||||
|
||||
pub fn get_command_path(command: &str) -> Result<PathBuf, RudoError> {
|
||||
if let Ok(path) = which(command) {
|
||||
Ok(path)
|
||||
} else {
|
||||
let mut path = PathBuf::new();
|
||||
path.push(command.to_owned());
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_username() -> Result<String, RudoError> {
|
||||
if let Some(username) = get_current_username() {
|
||||
if let Ok(username) = username.into_string() {
|
||||
Ok(username)
|
||||
} else {
|
||||
Err(RudoError::BadUsername)
|
||||
}
|
||||
} else {
|
||||
Err(RudoError::BadUsername)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_groups() -> Vec<Group> {
|
||||
if let Some(groups) =
|
||||
users::get_user_groups(&get_username().unwrap_or_default(), get_current_gid())
|
||||
{
|
||||
groups
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_hostname() -> Result<String, RudoError> {
|
||||
let mut hostname = [0u8; 255];
|
||||
unsafe {
|
||||
if gethostname(hostname.as_mut_ptr() as *mut i8, hostname.len()) != 0 {
|
||||
return Err(RudoError::BadHostname);
|
||||
}
|
||||
};
|
||||
if let Ok(s) = String::from_utf8(hostname.to_vec()) {
|
||||
let s = s.trim_matches(char::from_u32(0).unwrap()).to_owned();
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(RudoError::BadHostname)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue