From 84f1095a52a13cdc760b7b06956b89a134a45e98 Mon Sep 17 00:00:00 2001 From: LeChatP Date: Thu, 29 Aug 2024 11:55:40 +0200 Subject: [PATCH] chore: move all common sources + first packaging commit Common sources are now in a separate crate. This has the advantage to avoid many "unused" warnings. Indeed as chsr uses some calls that sr don't use, it raise this warning. And as the same with sr. So many unused warnings were wrongly reported. I started to make deployment work, as sr and chsr seems to be well-tested and made some security checks. For now, deployment is WIP. --- .github/workflows/pkg.yml | 37 ++++ Cargo.toml | 54 +++++- build.rs | 18 ++ makepkg.sh | 26 +++ rar-common/Cargo.toml | 46 +++++ {src => rar-common/src}/api.rs | 4 +- rar-common/src/config.rs | 0 {src => rar-common/src}/database/finder.rs | 25 ++- {src => rar-common/src}/database/migration.rs | 2 +- {src => rar-common/src}/database/mod.rs | 25 ++- {src => rar-common/src}/database/options.rs | 6 +- {src => rar-common/src}/database/structs.rs | 5 +- .../src/database/versionning.rs | 4 +- {src => rar-common/src}/database/wrapper.rs | 0 src/config.rs => rar-common/src/lib.rs | 24 +-- {src => rar-common/src}/plugin/hashchecker.rs | 4 +- {src => rar-common/src}/plugin/hierarchy.rs | 4 +- {src => rar-common/src}/plugin/mod.rs | 2 +- {src => rar-common/src}/plugin/ssd.rs | 12 +- {src => rar-common/src}/util.rs | 168 +++++++++++++++++- rar-common/src/version.rs | 4 + resources/arch/PKGBUILD | 43 +++++ resources/{ => arch}/arch_sr_pam.conf | 0 resources/arch/rootasrole.install | 41 +++++ resources/{ => debian}/deb_sr_pam.conf | 0 resources/debian/postinst | 63 +++++++ resources/{ => rh}/rh_sr_pam.conf | 0 src/chsr/cli/data.rs | 2 +- src/chsr/cli/mod.rs | 105 +++++------ src/chsr/cli/pair.rs | 23 ++- src/chsr/cli/process.rs | 6 +- src/chsr/cli/process/json.rs | 96 +++++----- src/chsr/cli/usage.rs | 6 +- src/chsr/main.rs | 20 +-- src/chsr/util.rs | 2 +- src/mod.rs | 167 ----------------- src/sr/main.rs | 36 ++-- src/sr/pam/mod.rs | 10 +- src/sr/timeout.rs | 5 +- xtask/Cargo.toml | 7 +- xtask/src/install.rs | 123 +++++++++++++ xtask/src/main.rs | 3 + 42 files changed, 822 insertions(+), 406 deletions(-) create mode 100644 .github/workflows/pkg.yml create mode 100755 makepkg.sh create mode 100644 rar-common/Cargo.toml rename {src => rar-common/src}/api.rs (98%) create mode 100644 rar-common/src/config.rs rename {src => rar-common/src}/database/finder.rs (99%) rename {src => rar-common/src}/database/migration.rs (99%) rename {src => rar-common/src}/database/mod.rs (93%) rename {src => rar-common/src}/database/options.rs (99%) rename {src => rar-common/src}/database/structs.rs (99%) rename src/database/version.rs => rar-common/src/database/versionning.rs (94%) rename {src => rar-common/src}/database/wrapper.rs (100%) rename src/config.rs => rar-common/src/lib.rs (96%) rename {src => rar-common/src}/plugin/hashchecker.rs (99%) rename {src => rar-common/src}/plugin/hierarchy.rs (99%) rename {src => rar-common/src}/plugin/mod.rs (77%) rename {src => rar-common/src}/plugin/ssd.rs (96%) rename {src => rar-common/src}/util.rs (64%) create mode 100644 rar-common/src/version.rs create mode 100644 resources/arch/PKGBUILD rename resources/{ => arch}/arch_sr_pam.conf (100%) create mode 100644 resources/arch/rootasrole.install rename resources/{ => debian}/deb_sr_pam.conf (100%) create mode 100644 resources/debian/postinst rename resources/{ => rh}/rh_sr_pam.conf (100%) delete mode 100644 src/mod.rs create mode 100644 xtask/src/install.rs diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml new file mode 100644 index 00000000..b6e339ef --- /dev/null +++ b/.github/workflows/pkg.yml @@ -0,0 +1,37 @@ +name: Deploy pkg to GitHub Packages + +## only triger manual +on: + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + components: clippy + override: true + + - name: Install Dependencies + run: ./dependencies.sh -yd + + - name: Configure + run: sudo ./configure.sh -yd + + - name: Install cargo deb + run: cargo install cargo-deb + + - name: Build + run: cargo deb + + - name: Upload to GitHub \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index b561146c..2bd01e76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["xtask", "capable", "capable-common"] +members = ["xtask", "capable", "capable-common", "rar-common"] [package] name = "RootAsRole" @@ -9,11 +9,13 @@ rust-version = "1.74.1" authors = ["Eddie Billoir "] edition = "2021" default-run = "sr" -description = "RootAsRole is an alternative to sudo that uses Linux capabilities and RBAC for scalability." -license-file = "LICENSE" +description = "An alternative to sudo that uses Linux capabilities and Role based access control." +license = "GPL-3.0-or-later" repository = "https://github.com/LeChatP/RootAsRole" +homepage = "https://lechatp.github.io/RootAsRole/" keywords = ["sudo", "capabilities", "rbac", "linux", "security"] categories = ["command-line-utilities", "os::linux-apis", "config"] +exclude = ["sudoers-reader/*", "book/*"] [badges] maintainance ={ status = "actively-maintained", badge = "https://img.shields.io/badge/maintenance-actively%20maintained-brightgreen.svg" } @@ -46,12 +48,13 @@ serde_json = "1.0.116" toml = "0.8.13" [dependencies] +rar-common = { path = "rar-common" } tracing = "0.1.40" tracing-subscriber = "0.3.18" libc = "0.2.155" -strum = { version = "0.26.2", features = ["derive"] } +strum = { version = "0.26.3", features = ["derive"] } semver = { version = "1.0.23", features = ["serde"] } -nix = { version = "0.28.0", features = ["user","process", "signal", "fs"] } +nix = { version = "0.29.0", features = ["user","process", "signal", "fs"] } #sudoers-reader = { path = "sudoers-reader" } capctl = "0.2.4" pcre2 = "0.2.7" @@ -86,3 +89,44 @@ tracing-subscriber = { version = "0.3.16", default-features = false, features = pest-test-gen = "0.1.7" pest-test = "0.1.6" lazy_static = "1.4.0" + + +[package.metadata.deb] +maintainer = "Eddie Billoir " +license-file = "LICENSE" +depends = "libpam0g, e2fsprogs, libcap2-bin, libpam-modules, libpcre2-8-0" +section = "admin" +priority = "optional" +assets = [ + ["target/release/sr", "usr/bin/sr", "0555"], + ["target/release/chsr", "usr/bin/chsr", "0555"], + ["resources/rootasrole.json", "usr/share/rootasrole/default.json", "0640"], + ["resources/debian/deb_sr_pam.conf", "usr/share/rootasrole/pam_sr.conf", "0644"] +] +conf-files = ["/etc/pam.d/sr"] +maintainer-scripts = "resources/debian/" +extended-description = "RootAsRole is a project to allow Linux/Unix administrators to delegate their administrative tasks access rights to multiple co-administrators through RBAC model and Linux Capabilities features." + +[package.metadata.generate-rpm] +assets = [ + { source = "target/release/sr", target = "/usr/bin/sr", mode = "0555" }, + { source = "target/release/chsr", target = "/usr/bin/chsr", mode = "0555" }, + { source = "resources/rootasrole.json", target = "/etc/security/rootasrole.json", mode = "0640" } +] + +[package.metadata.generate-rpm.requires] +libcap = "*" +e2fsprogs = "*" +coreutils = "*" +gawk = "*" +sed = "*" + +[package.metadata.aur] +depends = ["libcap", "e2fsprogs", "pcre2", "pam"] +files = [ ["target/release/sr", "/usr/bin/sr"], + ["target/release/chsr", "/usr/bin/chsr"], + ["resources/arch_sr_pam.conf", "/usr/share/rootasrole/pam_sr.conf"], + ["resources/rootasrole.json", "/usr/share/rootasrole/default.json"], + ["resources/debian/postinst", "/usr/share/rootasrole/postinst" ] ] +custom = [ "$pkgdir/usr/share/rootasrole/postinst" ] + diff --git a/build.rs b/build.rs index aa7b7a7e..ebb71470 100644 --- a/build.rs +++ b/build.rs @@ -38,6 +38,22 @@ fn set_cargo_version(package_version: &str, file: &str) -> Result<(), Box Result<(), Box> { + let pkgbuild = File::open(std::path::Path::new(file)).expect("PKGBUILD not found"); + let reader = BufReader::new(pkgbuild); + let lines = reader.lines().map(|l| l.unwrap()).collect::>(); + let mut pkgbuild = File::create(std::path::Path::new(file)).expect("PKGBUILD not found"); + for line in lines { + if line.starts_with("pkgver") { + writeln!(pkgbuild, "pkgver={}", package_version)?; + } else { + writeln!(pkgbuild, "{}", line)?; + } + } + pkgbuild.sync_all()?; + Ok(()) +} + fn write_doc(f: &mut File) -> Result<(), Box> { let docresp = reqwest::blocking::get( "https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/plain/man7/capabilities.7", @@ -171,4 +187,6 @@ fn main() { // } f.flush().unwrap(); + + } diff --git a/makepkg.sh b/makepkg.sh new file mode 100755 index 00000000..ad9b22d2 --- /dev/null +++ b/makepkg.sh @@ -0,0 +1,26 @@ +#/bin/sh + +# This script build every package for Arch Linux, Debian, Fedora. + +cargo build --release --bin sr --bin chsr || exit 1 + +# Arch Linux +if [ -z "$ARCH" ]; then + ARCH=$(uname -m) +fi +PKGEXT=.pkg.tar.zst + + +mkdir -p target/arch/usr/bin +mkdir -p target/arch/etc/pam.d +mkdir -p target/arch/usr/share/rootasrole +cp target/release/sr target/release/chsr target/arch/usr/bin +cp resources/rootasrole.json target/arch/usr/share/rootasrole/default.json +cp resources/arch/arch_sr_pam.conf target/arch/etc/pam.d/sr +cp resources/arch/PKGBUILD resources/arch/rootasrole.install target/arch + +sed -i "s/%ARCH%/$ARCH/g" target/arch/PKGBUILD + +cd target/arch + +makepkg -f -p PKGBUILD diff --git a/rar-common/Cargo.toml b/rar-common/Cargo.toml new file mode 100644 index 00000000..62a653f0 --- /dev/null +++ b/rar-common/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "rar-common" +version = "0.1.0" +edition = "2021" + +[dependencies] +tracing = "0.1.40" +tracing-subscriber = "0.3.18" +libc = "0.2.155" +strum = { version = "0.26.3", features = ["derive"] } +semver = { version = "1.0.23", features = ["serde"] } +nix = { version = "0.29.0", features = ["user","process", "signal", "fs"] } +#sudoers-reader = { path = "sudoers-reader" } +capctl = "0.2.4" +pcre2 = "0.2.7" +serde = { version = "1.0.202", features=["rc"] } +serde_json = "1.0.117" +ciborium = "0.2.2" +glob = "0.3.1" +pam-client = { version = "0.5.0", git = "https://gitlab.com/LeChatP/rust-pam-client.git" } +pam-sys = "1.0.0-alpha5" +bitflags = { version = "2.5.0" } +shell-words = "1.1.0" +syslog-tracing = "0.3.0" +linked_hash_set = { version = "0.1.4" } +derivative = "2.2.0" +sha2 = "0.10.8" +sha1 = "0.10.6" +md5 = "0.7.0" +chrono = "0.4.37" +pty-process = "0.4.0" +once_cell = "1.19.0" +pest = "2.7.8" +pest_derive = "2.7.8" +phf = { version = "0.11.2", features = ["macros"] } +const_format = "0.2.32" +hex = "0.4.3" + +[dev-dependencies] +env_logger = "*" +test-log = { version = "0.2.12", features = ["trace"] } +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.16", default-features = false, features = ["env-filter", "fmt"] } +pest-test-gen = "0.1.7" +pest-test = "0.1.6" +lazy_static = "1.4.0" \ No newline at end of file diff --git a/src/api.rs b/rar-common/src/api.rs similarity index 98% rename from src/api.rs rename to rar-common/src/api.rs index 05d6a5c1..a1ff9ae8 100644 --- a/src/api.rs +++ b/rar-common/src/api.rs @@ -6,9 +6,9 @@ use serde_json::Value; use strum::EnumIs; use tracing::debug; -use crate::common::database::finder::{Cred, ExecSettings, TaskMatch, UserMin}; +use crate::database::finder::{Cred, ExecSettings, TaskMatch, UserMin}; -use super::database::{ +use crate::database::{ finder::FilterMatcher, structs::{SActor, SConfig, SRole, STask}, }; diff --git a/rar-common/src/config.rs b/rar-common/src/config.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/database/finder.rs b/rar-common/src/database/finder.rs similarity index 99% rename from src/database/finder.rs rename to rar-common/src/database/finder.rs index 8446d963..db4eb3e3 100644 --- a/src/database/finder.rs +++ b/rar-common/src/database/finder.rs @@ -17,20 +17,15 @@ use pcre2::bytes::RegexBuilder; use strum::EnumIs; use tracing::{debug, warn}; -use crate::{ - as_borrow, - common::{ - api::{PluginManager, PluginResultAction}, - database::{ - options::{Opt, OptStack}, - structs::{ - SActor, SActorType, SCommand, SCommands, SConfig, SGroups, SRole, STask, - SetBehavior, - }, +use crate::{api::{PluginManager, PluginResultAction}, as_borrow}; +use crate::database::{ + options::{Opt, OptStack}, + structs::{ + SActor, SActorType, SCommand, SCommands, SConfig, SGroups, SRole, STask, + SetBehavior, }, - util::capabilities_are_exploitable, - }, -}; + }; +use crate::util::capabilities_are_exploitable; use bitflags::bitflags; #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -1000,11 +995,11 @@ mod tests { use test_log::test; use crate::{ - common::database::{ + database::{ make_weak_config, options::{EnvBehavior, PathBehavior, SAuthentication, SBounding, SPrivileged}, structs::IdTask, - version::Versioning, + versionning::Versioning, }, rc_refcell, }; diff --git a/src/database/migration.rs b/rar-common/src/database/migration.rs similarity index 99% rename from src/database/migration.rs rename to rar-common/src/database/migration.rs index 04e044c7..02dc1811 100644 --- a/src/database/migration.rs +++ b/rar-common/src/database/migration.rs @@ -3,7 +3,7 @@ use std::error::Error; use semver::Version; use tracing::debug; -use crate::common::version::PACKAGE_VERSION; +use crate::version::PACKAGE_VERSION; pub struct Migration { pub from: fn() -> Version, diff --git a/src/database/mod.rs b/rar-common/src/database/mod.rs similarity index 93% rename from src/database/mod.rs rename to rar-common/src/database/mod.rs index 8806a98b..ae90b31a 100644 --- a/src/database/mod.rs +++ b/rar-common/src/database/mod.rs @@ -1,30 +1,29 @@ use std::{cell::RefCell, error::Error, rc::Rc}; -use crate::common::config::save_settings; -use crate::common::util::{toggle_lock_config, ImmutableLock}; -use crate::common::version::PACKAGE_VERSION; +use crate::save_settings; +use crate::util::{toggle_lock_config, ImmutableLock}; +use crate::version::PACKAGE_VERSION; use chrono::Duration; use linked_hash_set::LinkedHashSet; use serde::{de, Deserialize, Serialize}; use tracing::debug; -use self::{migration::Migration, options::EnvKey, structs::SConfig, version::Versioning}; +use self::{migration::Migration, options::EnvKey, structs::SConfig, versionning::Versioning}; -use super::config::SettingsFile; -use super::util::warn_if_mutable; -use super::{ - config::{RemoteStorageSettings, ROOTASROLE}, - immutable_effective, - util::parse_capset_iter, +use crate::SettingsFile; +use crate::util::warn_if_mutable; +use crate::{ + RemoteStorageSettings, ROOTASROLE, + util::{parse_capset_iter, immutable_effective}, }; -use super::{open_with_privileges, write_json_config}; +use crate::{open_with_privileges, write_json_config}; pub mod finder; pub mod migration; pub mod options; pub mod structs; -pub mod version; +pub mod versionning; pub mod wrapper; pub fn make_weak_config(config: &Rc>) { @@ -72,7 +71,7 @@ pub fn read_json_config( if Migration::migrate( &versionned_config.version, &mut *config.as_ref().borrow_mut(), - version::JSON_MIGRATIONS, + versionning::JSON_MIGRATIONS, )? { save_json(settings.clone(), config.clone())?; } diff --git a/src/database/options.rs b/rar-common/src/database/options.rs similarity index 99% rename from src/database/options.rs rename to rar-common/src/database/options.rs index bc99f4e2..8c03730e 100644 --- a/src/database/options.rs +++ b/rar-common/src/database/options.rs @@ -1181,9 +1181,9 @@ mod tests { use nix::unistd::User; use crate::as_borrow_mut; - use crate::common::database::wrapper::SConfigWrapper; - use crate::common::database::wrapper::SRoleWrapper; - use crate::common::database::wrapper::STaskWrapper; + use crate::database::wrapper::SConfigWrapper; + use crate::database::wrapper::SRoleWrapper; + use crate::database::wrapper::STaskWrapper; use crate::rc_refcell; use super::super::options::*; diff --git a/src/database/structs.rs b/rar-common/src/database/structs.rs similarity index 99% rename from src/database/structs.rs rename to rar-common/src/database/structs.rs index 61489c6f..dbed1c8e 100644 --- a/src/database/structs.rs +++ b/rar-common/src/database/structs.rs @@ -20,9 +20,8 @@ use std::{ rc::{Rc, Weak}, }; -use crate::common::database::is_default; - use super::{ + is_default, options::Opt, wrapper::{OptWrapper, STaskWrapper}, }; @@ -684,7 +683,7 @@ mod tests { use crate::{ as_borrow, - common::database::options::{EnvBehavior, PathBehavior, SAuthentication, TimestampType}, + database::options::{EnvBehavior, PathBehavior, SAuthentication, TimestampType}, }; use super::*; diff --git a/src/database/version.rs b/rar-common/src/database/versionning.rs similarity index 94% rename from src/database/version.rs rename to rar-common/src/database/versionning.rs index adc59736..d13182e2 100644 --- a/src/database/version.rs +++ b/rar-common/src/database/versionning.rs @@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize}; use std::fmt::Debug; use super::migration::Migration; -use crate::common::config::SettingsFile; -use crate::common::version; +use crate::SettingsFile; +use crate::version; use super::structs::*; diff --git a/src/database/wrapper.rs b/rar-common/src/database/wrapper.rs similarity index 100% rename from src/database/wrapper.rs rename to rar-common/src/database/wrapper.rs diff --git a/src/config.rs b/rar-common/src/lib.rs similarity index 96% rename from src/config.rs rename to rar-common/src/lib.rs index a0532a77..10c03bc0 100644 --- a/src/config.rs +++ b/rar-common/src/lib.rs @@ -57,19 +57,23 @@ use std::{cell::RefCell, error::Error, ffi::OsStr, path::PathBuf, rc::Rc}; use serde::{Deserialize, Serialize}; use tracing::debug; -use crate::{ - common::{ +pub mod util; +pub mod database; +pub mod api; +pub mod version; +pub mod plugin; + + +use util::{ dac_override_effective, open_with_privileges, read_effective, - util::{toggle_lock_config, ImmutableLock}, - write_json_config, - }, - rc_refcell, + toggle_lock_config, ImmutableLock, + write_json_config, }; -use super::database::{ +use database::{ migration::Migration, structs::SConfig, - version::{self, Versioning}, + versionning::{Versioning, JSON_MIGRATIONS, SETTINGS_MIGRATIONS}, }; #[derive(Serialize, Deserialize, Debug, Clone)] @@ -257,7 +261,7 @@ where if Migration::migrate( &value.version, &mut *settingsfile.as_ref().borrow_mut(), - version::SETTINGS_MIGRATIONS, + SETTINGS_MIGRATIONS, )? { Migration::migrate( &value.version, @@ -267,7 +271,7 @@ where .config .as_ref() .borrow_mut(), - version::JSON_MIGRATIONS, + JSON_MIGRATIONS, )?; save_settings(settingsfile.clone())?; } diff --git a/src/plugin/hashchecker.rs b/rar-common/src/plugin/hashchecker.rs similarity index 99% rename from src/plugin/hashchecker.rs rename to rar-common/src/plugin/hashchecker.rs index 896f52dc..52cef494 100644 --- a/src/plugin/hashchecker.rs +++ b/rar-common/src/plugin/hashchecker.rs @@ -4,7 +4,7 @@ use nix::unistd::{access, AccessFlags}; use serde::{Deserialize, Serialize}; use tracing::{debug, warn}; -use crate::common::{ +use crate::{ api::PluginManager, database::{ finder::{final_path, parse_conf_command}, @@ -128,7 +128,7 @@ mod tests { use super::*; use crate::{ - common::database::{ + database::{ finder::{Cred, TaskMatcher}, structs::{IdTask, SActor, SCommand, SCommands, SConfig, SRole, STask}, }, diff --git a/src/plugin/hierarchy.rs b/rar-common/src/plugin/hierarchy.rs similarity index 99% rename from src/plugin/hierarchy.rs rename to rar-common/src/plugin/hierarchy.rs index 8fa3f0f9..352de2a3 100644 --- a/src/plugin/hierarchy.rs +++ b/rar-common/src/plugin/hierarchy.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use crate::common::{ +use crate::{ api::{PluginManager, PluginResultAction}, database::{ finder::{Cred, FilterMatcher, TaskMatch, TaskMatcher}, @@ -81,7 +81,7 @@ mod tests { use super::*; use crate::{ - common::database::{ + database::{ finder::UserMin, structs::{IdTask, SActor, SCommand, SCommands, SConfig, STask}, }, diff --git a/src/plugin/mod.rs b/rar-common/src/plugin/mod.rs similarity index 77% rename from src/plugin/mod.rs rename to rar-common/src/plugin/mod.rs index f1372d5f..e5384329 100644 --- a/src/plugin/mod.rs +++ b/rar-common/src/plugin/mod.rs @@ -2,7 +2,7 @@ mod hashchecker; mod hierarchy; mod ssd; -pub(crate) fn register_plugins() { +pub fn register_plugins() { hashchecker::register(); ssd::register(); hierarchy::register(); diff --git a/src/plugin/ssd.rs b/rar-common/src/plugin/ssd.rs similarity index 96% rename from src/plugin/ssd.rs rename to rar-common/src/plugin/ssd.rs index 687e030b..8ffe10ea 100644 --- a/src/plugin/ssd.rs +++ b/rar-common/src/plugin/ssd.rs @@ -6,12 +6,10 @@ use serde_json::Error; use crate::{ as_borrow, - common::{ - api::{PluginManager, PluginResult}, - database::{ - finder::Cred, - structs::{SActor, SConfig, SGroups, SRole}, - }, + api::{PluginManager, PluginResult}, + database::{ + finder::Cred, + structs::{SActor, SConfig, SGroups, SRole}, }, }; @@ -146,7 +144,7 @@ mod tests { use super::*; use crate::{ - common::database::structs::{SActor, SConfig, SRole}, + database::structs::{SActor, SConfig, SRole}, rc_refcell, }; use nix::unistd::{Group, Pid}; diff --git a/src/util.rs b/rar-common/src/util.rs similarity index 64% rename from src/util.rs rename to rar-common/src/util.rs index 11ea73a4..af610cb9 100644 --- a/src/util.rs +++ b/rar-common/src/util.rs @@ -1,20 +1,20 @@ -use std::{error::Error, fs::File, os::fd::AsRawFd, path::PathBuf}; +use std::{error::Error, fs::File, os::fd::AsRawFd, path::PathBuf, ffi::CString, path::Path}; use capctl::{Cap, CapSet, ParseCapError}; use libc::{FS_IOC_GETFLAGS, FS_IOC_SETFLAGS}; use strum::EnumIs; -use tracing::{debug, warn}; +use tracing::{debug, warn, Level}; +use capctl::{prctl, CapState}; +use serde::Serialize; +use tracing_subscriber::util::SubscriberInitExt; -use crate::common::{ - dac_override_effective, fowner_effective, immutable_effective, open_with_privileges, - read_effective, -}; pub const RST: &str = "\x1B[0m"; pub const BOLD: &str = "\x1B[1m"; pub const UNDERLINE: &str = "\x1B[4m"; pub const RED: &str = "\x1B[31m"; + #[macro_export] macro_rules! upweak { ($e:expr) => { @@ -162,12 +162,164 @@ fn remove_outer_quotes(input: &str) -> String { } } +#[cfg(debug_assertions)] +pub fn subsribe(tool: &str) { + use std::io; + let identity = CString::new(tool).unwrap(); + let options = syslog_tracing::Options::LOG_PID; + let facility = syslog_tracing::Facility::Auth; + let _syslog = syslog_tracing::Syslog::new(identity, options, facility).unwrap(); + tracing_subscriber::fmt() + .with_max_level(Level::DEBUG) + .with_file(true) + .with_line_number(true) + .with_writer(io::stdout) + .finish() + .init(); +} + +#[cfg(not(debug_assertions))] +pub fn subsribe(tool: &str) { + use std::panic::set_hook; + + let identity = CString::new(tool).unwrap(); + let options = syslog_tracing::Options::LOG_PID; + let facility = syslog_tracing::Facility::Auth; + let syslog = syslog_tracing::Syslog::new(identity, options, facility).unwrap(); + tracing_subscriber::fmt() + .compact() + .with_max_level(Level::WARN) + .with_file(false) + .with_timer(false) + .with_line_number(false) + .with_target(false) + .without_time() + .with_writer(syslog) + .finish() + .init(); + set_hook(Box::new(|info| { + if let Some(s) = info.payload().downcast_ref::() { + println!("{}", s); + } + })); +} + +pub fn drop_effective() -> Result<(), capctl::Error> { + let mut current = CapState::get_current()?; + current.effective.clear(); + current.set_current() +} + +pub fn cap_effective(cap: Cap, enable: bool) -> Result<(), capctl::Error> { + let mut current = CapState::get_current()?; + current.effective.set_state(cap, enable); + current.set_current() +} + +pub fn setpcap_effective(enable: bool) -> Result<(), capctl::Error> { + cap_effective(Cap::SETPCAP, enable) +} + +pub fn setuid_effective(enable: bool) -> Result<(), capctl::Error> { + cap_effective(Cap::SETUID, enable) +} + +pub fn setgid_effective(enable: bool) -> Result<(), capctl::Error> { + cap_effective(Cap::SETGID, enable) +} + +pub fn fowner_effective(enable: bool) -> Result<(), capctl::Error> { + cap_effective(Cap::FOWNER, enable) +} + +pub fn read_effective(enable: bool) -> Result<(), capctl::Error> { + cap_effective(Cap::DAC_READ_SEARCH, enable) +} + +pub fn dac_override_effective(enable: bool) -> Result<(), capctl::Error> { + cap_effective(Cap::DAC_OVERRIDE, enable) +} + +pub fn immutable_effective(enable: bool) -> Result<(), capctl::Error> { + cap_effective(Cap::LINUX_IMMUTABLE, enable) +} + +pub fn activates_no_new_privs() -> Result<(), capctl::Error> { + prctl::set_no_new_privs() +} + +pub fn write_json_config(settings: &T, path: S) -> Result<(), Box> +where + S: std::convert::AsRef + Clone, +{ + let file = create_with_privileges(path)?; + serde_json::to_writer_pretty(file, &settings)?; + Ok(()) +} + +pub fn create_with_privileges>(p: P) -> Result { + std::fs::File::create(&p).or_else(|e| { + debug!( + "Error creating file without privilege, trying with privileges: {}", + e + ); + dac_override_effective(true)?; + let res = std::fs::File::create(p).inspect_err(|e| { + debug!( + "Error creating file without privilege, trying with privileges: {}", + e + ); + }); + dac_override_effective(false)?; + res + }) +} + +pub fn open_with_privileges>(p: P) -> Result { + std::fs::File::open(&p).or_else(|e| { + debug!( + "Error creating file without privilege, trying with privileges: {}", + e + ); + read_effective(true).or(dac_override_effective(true))?; + let res = std::fs::File::open(p); + read_effective(false)?; + dac_override_effective(false)?; + res + }) +} + +pub fn remove_with_privileges>(p: P) -> Result<(), std::io::Error> { + std::fs::remove_file(&p).or_else(|e| { + debug!( + "Error creating file without privilege, trying with privileges: {}", + e + ); + dac_override_effective(true)?; + let res = std::fs::remove_file(p); + dac_override_effective(false)?; + res + }) +} + +pub fn create_dir_all_with_privileges>(p: P) -> Result<(), std::io::Error> { + std::fs::create_dir_all(&p).or_else(|e| { + debug!( + "Error creating file without privilege, trying with privileges: {}", + e + ); + dac_override_effective(true)?; + let res = std::fs::create_dir_all(p); + read_effective(false)?; + dac_override_effective(false)?; + res + }) +} + #[cfg(test)] mod test { use std::fs; - use capctl::CapState; - use super::*; #[test] diff --git a/rar-common/src/version.rs b/rar-common/src/version.rs new file mode 100644 index 00000000..9472607f --- /dev/null +++ b/rar-common/src/version.rs @@ -0,0 +1,4 @@ +// This file is generated by build.rs +// Do not edit this file directly +// Instead edit build.rs and run cargo build +pub const PACKAGE_VERSION: &'static str = "3.0.0-alpha.5"; diff --git a/resources/arch/PKGBUILD b/resources/arch/PKGBUILD new file mode 100644 index 00000000..adce32ca --- /dev/null +++ b/resources/arch/PKGBUILD @@ -0,0 +1,43 @@ +# Maintainer: Eddie Billoir + +pkgname=rootasrole +pkgver=3.0.0_alpha.5 +pkgrel=1 +pkgdesc='Alternative to sudo to run some administrative commands that uses Linux capabilities and RBAC for scalability.' +url='https://lechatp.github.io/RootAsRole/' +license=('GPL-3.0-or-later') +arch=('x86_64') +source=("https://github.com/LeChatP/RootAsRole/archive/v${pkgver//_/-}.tar.gz") +sha256sums=('SKIP') +depends=('libcap' 'e2fsprogs' 'pcre2' 'pam') +backup=('etc/pam.d/sr' 'etc/security/rootasrole.json') +validpgpkeys=('74F43C5774BE1F3527DEFA4835C155EA0525104D') +makedepends=(cargo) +source=("$pkgname-$pkgver.tar.gz::https://static.crates.io/crates/$pkgname/$pkgname-$pkgver.crate") +#source=('https://github.com/LeChatP/RootAsRole/releases/download/v${pkgver//_/-}/RootAsRole-${pkgver//_/-}-$arch.tar.gz') +install=rootasrole.install + +prepare() { + export RUSTUP_TOOLCHAIN=stable + cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')" +} + +build() { + export RUSTUP_TOOLCHAIN=stable + export CARGO_TARGET_DIR=target + cargo build --frozen --release --all-features +} + +check() { + export RUSTUP_TOOLCHAIN=stable + cargo test --frozen --all-features +} + +package() { + cd $pkgname-$pkgver + install -Dm755 'target/release/sr' -t "$pkgdir/usr/bin" + install -Dm755 'target/release/chsr' -t "$pkgdir/usr/bin" + install -Dm644 'resources/arch/arch_sr_pam.conf' -t "$pkgdir/etc/pam.d/sr" + install -Dm644 'resources/rootasrole.json' -t "$pkgdir/usr/share/rootasrole/default.json" + setcap '=p' "$pkgdir/usr/bin/sr" +} diff --git a/resources/arch_sr_pam.conf b/resources/arch/arch_sr_pam.conf similarity index 100% rename from resources/arch_sr_pam.conf rename to resources/arch/arch_sr_pam.conf diff --git a/resources/arch/rootasrole.install b/resources/arch/rootasrole.install new file mode 100644 index 00000000..b628359f --- /dev/null +++ b/resources/arch/rootasrole.install @@ -0,0 +1,41 @@ +TARGET_PATH="/etc/security/rootasrole.json" + +log() { + echo "RootAsRole: $1" +} + +post_install() { + if [ ! -f "$TARGET_PATH" ]; then + cp "/usr/share/rootasrole/default.json" "$TARGET_PATH" || log "Failed to copy the default configuration file to $TARGET_PATH" && exit 1 + else + log "The configuration file $TARGET_PATH already exists. Skipping the post-installation process." + return 0 + fi + + # Check the file system type + FS_TYPE=$(df -T "$TARGET_PATH" | awk 'NR==2 {print $2}') + + # Supported file systems for immutable flag + # It may not work on all file systems, but it is supported on the most common ones. + case "$FS_TYPE" in + ext2|ext3|ext4|xfs|btrfs|ocfs2|jfs|reiserfs) + if ! grep -q '"immutable": true' "$TARGET_PATH"; then + sed -i 's/"immutable": false/"immutable": true/' "$TARGET_PATH" + log "The file $TARGET_PATH is now immutable, and sr will check that immutable is enforced before executing." + fi + # Attempt to set the immutable flag + if ! chattr +i "$TARGET_PATH"; then + log "Failed to set the immutable flag on $TARGET_PATH" + sed -i 's/"immutable": true/"immutable": false/' "$TARGET_PATH" + sed -i "s;\"CAP_LINUX_IMMUTABLE\";;g" "$TARGET_PATH" + fi + ;; + *) + log "The file system $FS_TYPE does not support the immutable flag. Avoid checking the immutable flag during sr execution." + sed -i "s/\"immutable\": true/\"immutable\": false/g" "$TARGET_PATH" + sed -i "s;\"CAP_LINUX_IMMUTABLE\";;g" "$TARGET_PATH" + return 1 + ;; + esac +} + diff --git a/resources/deb_sr_pam.conf b/resources/debian/deb_sr_pam.conf similarity index 100% rename from resources/deb_sr_pam.conf rename to resources/debian/deb_sr_pam.conf diff --git a/resources/debian/postinst b/resources/debian/postinst new file mode 100644 index 00000000..bfd4ccbc --- /dev/null +++ b/resources/debian/postinst @@ -0,0 +1,63 @@ +#!/bin/sh +#DEBHELPER# +set -e + +log() { + echo "RootAsRole: $1" +} + + +configure() { + + TARGET_PATH="/etc/security/rootasrole.json" + + if [ ! -f "$TARGET_PATH" ]; then + cp "/usr/share/rootasrole/default.json" "$TARGET_PATH" || log "Failed to copy the default configuration file to $TARGET_PATH" && exit 1 + elif ! diff -q "/usr/share/rootasrole/default.json" "$TARGET_PATH" > /dev/null; then + return 0 + fi + + # Check the file system type + FS_TYPE=$(df -T "$TARGET_PATH" | awk 'NR==2 {print $2}') + + # Supported file systems for immutable flag + # It may not work on all file systems, but it is supported on the most common ones. + case "$FS_TYPE" in + ext2|ext3|ext4|xfs|btrfs|ocfs2|jfs|reiserfs) + if ! grep -q '"immutable": true' "$TARGET_PATH"; then + sed -i 's/"immutable": false/"immutable": true/' "$TARGET_PATH" + log "The file $TARGET_PATH is now immutable, and sr will check that immutable is enforced before executing." + fi + # Attempt to set the immutable flag + if ! chattr +i "$TARGET_PATH" > /dev/null 2>&1; then + log "Failed to set the immutable flag on $TARGET_PATH" + sed -i 's/"immutable": true/"immutable": false/' "$TARGET_PATH" + sed -i "s;\"CAP_LINUX_IMMUTABLE\";;g" "$TARGET_PATH" + fi + ;; + *) + log "The file system $FS_TYPE does not support the immutable flag. Avoid checking the immutable flag during sr execution." + sed -i "s/\"immutable\": true/\"immutable\": false/g" "$TARGET_PATH" + sed -i "s;\"CAP_LINUX_IMMUTABLE\";;g" "$TARGET_PATH" + exit 1 + ;; + esac +} + +setcap "=p" "/usr/bin/sr" || (log "Failed to set capabilities on /usr/bin/sr" && exit 1) + +case "$1" in + configure|abort-remove|abort-deconfigure) + configure + ;; + + abort-upgrade|upgrade|triggers-only|disappear) + exit 0 + ;; + + *) + log "postinst called with unknown argument \`$1'" + exit 1 + ;; +esac + diff --git a/resources/rh_sr_pam.conf b/resources/rh/rh_sr_pam.conf similarity index 100% rename from resources/rh_sr_pam.conf rename to resources/rh/rh_sr_pam.conf diff --git a/src/chsr/cli/data.rs b/src/chsr/cli/data.rs index 2cb3f9f2..b29480bd 100644 --- a/src/chsr/cli/data.rs +++ b/src/chsr/cli/data.rs @@ -4,7 +4,7 @@ use capctl::CapSet; use chrono::Duration; use linked_hash_set::LinkedHashSet; -use crate::common::database::{ +use rar_common::database::{ options::{ EnvBehavior, EnvKey, OptType, PathBehavior, SAuthentication, SBounding, SPrivileged, TimestampType, diff --git a/src/chsr/cli/mod.rs b/src/chsr/cli/mod.rs index c16300b1..6c65ee80 100644 --- a/src/chsr/cli/mod.rs +++ b/src/chsr/cli/mod.rs @@ -13,7 +13,8 @@ use process::process_input; use tracing::debug; use usage::print_usage; -use crate::{common::config::Storage, util::escape_parser_string_vec}; +use rar_common::Storage; +use crate::util::escape_parser_string_vec; pub fn main(storage: &Storage, args: I) -> Result> where @@ -40,19 +41,11 @@ where mod tests { use std::{io::Write, rc::Rc}; - use crate::common::{ - config, - database::{read_json_config, structs::SCredentials}, - remove_with_privileges, - }; - - use super::super::common::{ - config::{RemoteStorageSettings, SettingsFile, Storage, ROOTASROLE}, - database::{options::*, structs::*, version::Versioning}, + use rar_common::{ + database::{options::*, read_json_config, structs::{SCredentials, *}, versionning::Versioning}, get_settings, rc_refcell, util::remove_with_privileges, RemoteStorageSettings, SettingsFile, Storage, StorageMethod, ROOTASROLE }; use super::*; - use crate::rc_refcell; use capctl::Cap; use chrono::TimeDelta; use tracing::error; @@ -71,7 +64,7 @@ mod tests { let path = format!("{}.{}", ROOTASROLE, name); let mut file = std::fs::File::create(path.clone()).unwrap(); let mut settings = SettingsFile::default(); - settings.storage.method = config::StorageMethod::JSON; + settings.storage.method = StorageMethod::JSON; settings.storage.settings = Some(RemoteStorageSettings::default()); settings.storage.settings.as_mut().unwrap().path = Some(path.into()); settings.storage.settings.as_mut().unwrap().immutable = Some(false); @@ -223,7 +216,7 @@ mod tests { #[test] fn test_all_main() { setup("all_main"); - let settings = config::get_settings(&format!("{}.{}", ROOTASROLE, "all_main")) + let settings = get_settings(&format!("{}.{}", ROOTASROLE, "all_main")) .expect("Failed to get settings"); let config = read_json_config(settings.clone()).expect("Failed to read json"); assert!(main(&Storage::JSON(config.clone()), vec!["--help"],) @@ -262,7 +255,7 @@ mod tests { fn test_r_complete_show_actors() { setup("r_complete_show_actors"); let settings = - config::get_settings(&format!("{}.{}", ROOTASROLE, "r_complete_show_actors")) + get_settings(&format!("{}.{}", ROOTASROLE, "r_complete_show_actors")) .expect("Failed to get settings"); let config = read_json_config(settings.clone()).expect("Failed to read json"); assert!(main( @@ -314,7 +307,7 @@ mod tests { #[test] fn test_purge_tasks() { setup("purge_tasks"); - let settings = config::get_settings(&format!("{}.{}", ROOTASROLE, "purge_tasks")) + let settings = get_settings(&format!("{}.{}", ROOTASROLE, "purge_tasks")) .expect("Failed to get settings"); let config = read_json_config(settings.clone()).expect("Failed to read json"); assert!(main( @@ -333,7 +326,7 @@ mod tests { #[test] fn test_r_complete_purge_all() { setup("r_complete_purge_all"); - let settings = config::get_settings(&format!("{}.{}", ROOTASROLE, "r_complete_purge_all")) + let settings = get_settings(&format!("{}.{}", ROOTASROLE, "r_complete_purge_all")) .expect("Failed to get settings"); let config = read_json_config(settings.clone()).expect("Failed to read json"); assert!(main( @@ -352,7 +345,7 @@ mod tests { #[test] fn test_r_complete_grant_u_user1_g_group1_g_group2_group3() { setup("r_complete_grant_u_user1_g_group1_g_group2_group3"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_grant_u_user1_g_group1_g_group2_group3" )) @@ -415,7 +408,7 @@ mod tests { #[test] fn test_r_complete_task_t_complete_show_all() { setup("r_complete_task_t_complete_show_all"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_task_t_complete_show_all" )) @@ -470,7 +463,7 @@ mod tests { #[test] fn test_r_complete_task_t_complete_purge_cmd() { setup("r_complete_task_t_complete_purge_cmd"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_task_t_complete_purge_cmd" )) @@ -492,7 +485,7 @@ mod tests { #[test] fn test_r_complete_task_t_complete_purge_cred() { setup("r_complete_task_t_complete_purge_cred"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_task_t_complete_purge_cred" )) @@ -510,7 +503,7 @@ mod tests { }) .is_ok_and(|b| b)); debug!("====="); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_task_t_complete_purge_cred" )) @@ -552,7 +545,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_cmd_setpolicy_deny_all() { setup("r_complete_t_t_complete_cmd_setpolicy_deny_all"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_cmd_setpolicy_deny_all" )) @@ -582,7 +575,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_cmd_setpolicy_allow_all() { setup("r_complete_t_t_complete_cmd_setpolicy_allow_all"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_cmd_setpolicy_allow_all" )) @@ -612,7 +605,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_cmd_whitelist_add_super_command_with_spaces() { setup("r_complete_t_t_complete_cmd_whitelist_add_super_command_with_spaces"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_cmd_whitelist_add_super_command_with_spaces" )) @@ -674,7 +667,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_cmd_blacklist_del_super_command_with_spaces() { setup("r_complete_t_t_complete_cmd_blacklist_del_super_command_with_spaces"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_cmd_blacklist_del_super_command_with_spaces" )) @@ -715,7 +708,7 @@ mod tests { fn test_r_complete_t_t_complete_cred_set_caps_cap_dac_override_cap_sys_admin_cap_sys_boot_setuid_user1_setgid_group1_group2( ) { setup("r_complete_t_t_complete_cred_set_caps_cap_dac_override_cap_sys_admin_cap_sys_boot_setuid_user1_setgid_group1_group2"); - let settings = config::get_settings(&format!("{}.{}",ROOTASROLE,"r_complete_t_t_complete_cred_set_caps_cap_dac_override_cap_sys_admin_cap_sys_boot_setuid_user1_setgid_group1_group2")).expect("Failed to get settings"); + let settings = get_settings(&format!("{}.{}",ROOTASROLE,"r_complete_t_t_complete_cred_set_caps_cap_dac_override_cap_sys_admin_cap_sys_boot_setuid_user1_setgid_group1_group2")).expect("Failed to get settings"); let config = read_json_config(settings.clone()).expect("Failed to read json"); assert!(main(&Storage::JSON(config.clone()), "r complete t t_complete cred set --caps cap_dac_override,cap_sys_admin,cap_sys_boot --setuid user1 --setgid group1,group2".split(" "), ) @@ -823,7 +816,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_cred_caps_setpolicy_deny_all() { setup("r_complete_t_t_complete_cred_caps_setpolicy_deny_all"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_cred_caps_setpolicy_deny_all" )) @@ -856,7 +849,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_cred_caps_setpolicy_allow_all() { setup("r_complete_t_t_complete_cred_caps_setpolicy_allow_all"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_cred_caps_setpolicy_allow_all" )) @@ -890,7 +883,7 @@ mod tests { fn test_r_complete_t_t_complete_cred_caps_whitelist_add_cap_dac_override_cap_sys_admin_cap_sys_boot( ) { setup("r_complete_t_t_complete_cred_caps_whitelist_add_cap_dac_override_cap_sys_admin_cap_sys_boot"); - let settings = config::get_settings(&format!("{}.{}",ROOTASROLE,"r_complete_t_t_complete_cred_caps_whitelist_add_cap_dac_override_cap_sys_admin_cap_sys_boot")).expect("Failed to get settings"); + let settings = get_settings(&format!("{}.{}",ROOTASROLE,"r_complete_t_t_complete_cred_caps_whitelist_add_cap_dac_override_cap_sys_admin_cap_sys_boot")).expect("Failed to get settings"); let config = read_json_config(settings.clone()).expect("Failed to read json"); assert!(main(&Storage::JSON(config.clone()), "r complete t t_complete cred caps whitelist add cap_dac_override cap_sys_admin cap_sys_boot".split(" ")) .inspect_err(|e| { @@ -933,7 +926,7 @@ mod tests { fn test_r_complete_t_t_complete_cred_caps_blacklist_add_cap_dac_override_cap_sys_admin_cap_sys_boot( ) { setup("r_complete_t_t_complete_cred_caps_blacklist_add_cap_dac_override_cap_sys_admin_cap_sys_boot"); - let settings = config::get_settings(&format!("{}.{}",ROOTASROLE,"r_complete_t_t_complete_cred_caps_blacklist_add_cap_dac_override_cap_sys_admin_cap_sys_boot")).expect("Failed to get settings"); + let settings = get_settings(&format!("{}.{}",ROOTASROLE,"r_complete_t_t_complete_cred_caps_blacklist_add_cap_dac_override_cap_sys_admin_cap_sys_boot")).expect("Failed to get settings"); let config = read_json_config(settings.clone()).expect("Failed to read json"); assert!(main(&Storage::JSON(config.clone()), "r complete t t_complete cred caps blacklist add cap_dac_override cap_sys_admin cap_sys_boot".split(" "), ) @@ -1054,7 +1047,7 @@ mod tests { #[test] fn test_options_show_all() { setup("options_show_all"); - let settings = config::get_settings(&format!("{}.{}", ROOTASROLE, "options_show_all")) + let settings = get_settings(&format!("{}.{}", ROOTASROLE, "options_show_all")) .expect("Failed to get settings"); let config = read_json_config(settings.clone()).expect("Failed to read json"); assert!(main( @@ -1095,7 +1088,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_options_show_env() { setup("r_complete_t_t_complete_options_show_env"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_options_show_env" )) @@ -1161,7 +1154,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_path_setpolicy_delete_all() { setup("r_complete_t_t_complete_o_path_setpolicy_delete_all"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_path_setpolicy_delete_all" )) @@ -1196,7 +1189,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_path_setpolicy_keep_unsafe() { setup("r_complete_t_t_complete_o_path_setpolicy_keep_unsafe"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_path_setpolicy_keep_unsafe" )) @@ -1280,7 +1273,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_path_whitelist_add() { setup("r_complete_t_t_complete_o_path_whitelist_add"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_path_whitelist_add" )) @@ -1562,7 +1555,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_path_blacklist_purge() { setup("r_complete_t_t_complete_o_path_blacklist_purge"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_path_blacklist_purge" )) @@ -1584,7 +1577,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_keep_only_myvar_var2() { setup("r_complete_t_t_complete_o_env_keep_only_MYVAR_VAR2"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_keep_only_MYVAR_VAR2" )) @@ -1661,7 +1654,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_delete_only_myvar_var2() { setup("r_complete_t_t_complete_o_env_delete_only_MYVAR_VAR2"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_delete_only_MYVAR_VAR2" )) @@ -1738,7 +1731,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_set_myvar_value_var2_value2() { setup("r_complete_t_t_complete_o_env_set_MYVAR_value_VAR2_value2"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_set_MYVAR_value_VAR2_value2" )) @@ -1810,7 +1803,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_add_myvar_value_var2_value2() { setup("r_complete_t_t_complete_o_env_add_MYVAR_value_VAR2_value2"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_add_MYVAR_value_VAR2_value2" )) @@ -1989,7 +1982,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_setpolicy_delete_all() { setup("r_complete_t_t_complete_o_env_setpolicy_delete_all"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_setpolicy_delete_all" )) @@ -2026,7 +2019,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_setpolicy_keep_all() { setup("r_complete_t_t_complete_o_env_setpolicy_keep_all"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_setpolicy_keep_all" )) @@ -2063,7 +2056,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_setpolicy_inherit() { setup("r_complete_t_t_complete_o_env_setpolicy_inherit"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_setpolicy_inherit" )) @@ -2100,7 +2093,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_whitelist_add_myvar() { setup("r_complete_t_t_complete_o_env_whitelist_add_MYVAR"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_whitelist_add_MYVAR" )) @@ -2216,7 +2209,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_whitelist_purge() { setup("r_complete_t_t_complete_o_env_whitelist_purge"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_whitelist_purge" )) @@ -2251,7 +2244,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_blacklist_add_myvar() { setup("r_complete_t_t_complete_o_env_blacklist_add_MYVAR"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_blacklist_add_MYVAR" )) @@ -2310,7 +2303,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_blacklist_set_myvar() { setup("r_complete_t_t_complete_o_env_blacklist_set_MYVAR"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_blacklist_set_MYVAR" )) @@ -2361,7 +2354,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_blacklist_purge() { setup("r_complete_t_t_complete_o_env_blacklist_purge"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_blacklist_purge" )) @@ -2396,7 +2389,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_env_checklist_add_myvar() { setup("r_complete_t_t_complete_o_env_checklist_add_MYVAR"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_env_checklist_add_MYVAR" )) @@ -2522,7 +2515,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_root_privileged() { setup("r_complete_t_t_complete_o_root_privileged"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_root_privileged" )) @@ -2610,7 +2603,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_bounding_strict() { setup("r_complete_t_t_complete_o_bounding_strict"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_bounding_strict" )) @@ -2646,7 +2639,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_bounding_ignore() { setup("r_complete_t_t_complete_o_bounding_ignore"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_bounding_ignore" )) @@ -2682,7 +2675,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_bounding_inherit() { setup("r_complete_t_t_complete_o_bounding_inherit"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_bounding_inherit" )) @@ -2718,7 +2711,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_auth_skip() { setup("r_complete_t_t_complete_o_auth_skip"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_auth_skip" )) @@ -2806,7 +2799,7 @@ mod tests { #[test] fn test_r_complete_t_t_complete_o_wildcard_denied_set() { setup("r_complete_t_t_complete_o_wildcard_denied_set"); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_wildcard_denied_set" )) @@ -2890,7 +2883,7 @@ mod tests { "~" ); debug!("====="); - let settings = config::get_settings(&format!( + let settings = get_settings(&format!( "{}.{}", ROOTASROLE, "r_complete_t_t_complete_o_wildcard_denied_set" )) diff --git a/src/chsr/cli/pair.rs b/src/chsr/cli/pair.rs index 4d011027..5c58b508 100644 --- a/src/chsr/cli/pair.rs +++ b/src/chsr/cli/pair.rs @@ -6,15 +6,13 @@ use linked_hash_set::LinkedHashSet; use pest::iterators::Pair; use tracing::{debug, warn}; -use crate::{ - cli::data::{RoleType, TaskType}, - common::database::{ - options::{ - EnvBehavior, OptType, PathBehavior, SAuthentication, SBounding, SPrivileged, - TimestampType, - }, - structs::{IdTask, SActor, SActorType, SGroups, SetBehavior}, +use crate::cli::data::{RoleType, TaskType}; +use rar_common::database::{ + options::{ + EnvBehavior, OptType, PathBehavior, SAuthentication, SBounding, SPrivileged, + TimestampType, }, + structs::{IdTask, SActor, SActorType, SGroups, SetBehavior}, }; use super::data::*; @@ -423,13 +421,14 @@ mod test { data::RoleType, pair::{recurse_pair, Cli, InputAction, Rule}, }, - common::{ - database::structs::SActor, - util::{BOLD, RED, RST}, - }, util::underline, }; + use rar_common::{ + database::structs::SActor, + util::{BOLD, RED, RST}, + }; + use super::Inputs; fn make_args(args: &str) -> String { diff --git a/src/chsr/cli/process.rs b/src/chsr/cli/process.rs index f2efd11d..c75bbc54 100644 --- a/src/chsr/cli/process.rs +++ b/src/chsr/cli/process.rs @@ -6,8 +6,8 @@ use json::*; use tracing::debug; -use crate::common::{ - config::Storage, +use rar_common::{ + Storage, database::{ options::{Opt, OptType}, structs::IdTask, @@ -394,7 +394,7 @@ pub fn process_input(storage: &Storage, inputs: Inputs) -> Result>, + rconfig: &Rc>, role_id: Option, task_id: Option, exec_on_opt: impl Fn(Rc>) -> Result<(), Box>, diff --git a/src/chsr/cli/process/json.rs b/src/chsr/cli/process/json.rs index 1e344d24..36ff748e 100644 --- a/src/chsr/cli/process/json.rs +++ b/src/chsr/cli/process/json.rs @@ -9,22 +9,24 @@ use std::{ use linked_hash_set::LinkedHashSet; use tracing::debug; -use crate::{ - cli::data::{InputAction, RoleType, SetListType, TaskType, TimeoutOpt}, - common::database::{ - options::{ - EnvBehavior, EnvKey, Opt, OptStack, OptType, PathBehavior, SEnvOptions, SPathOptions, - STimeout, - }, - structs::{IdTask, SCapabilities, SCommand, SRole, STask}, +use crate::cli::data::{InputAction, RoleType, SetListType, TaskType, TimeoutOpt}; + +use rar_common::{ + database::{ + options::{ + EnvBehavior, EnvKey, Opt, OptStack, OptType, PathBehavior, SEnvOptions, SPathOptions, + STimeout, }, - rc_refcell, + structs::{IdTask, SCapabilities, SCommand, SRole, STask}, + +}, +rc_refcell, }; use super::perform_on_target_opt; pub fn list_json( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, options: bool, @@ -48,7 +50,7 @@ pub fn list_json( fn list_task( task_id: Option, - role: &Rc>, + role: &Rc>, options: bool, options_type: Option, task_type: Option, @@ -103,7 +105,7 @@ fn list_task( } fn print_task( - task: &std::rc::Rc>, + task: &std::rc::Rc>, task_type: TaskType, ) { match task_type { @@ -126,7 +128,7 @@ fn print_task( } fn print_role( - role: &std::rc::Rc>, + role: &std::rc::Rc>, role_type: &RoleType, ) { match role_type { @@ -149,7 +151,7 @@ fn print_role( } pub fn role_add_del( - rconfig: &Rc>, + rconfig: &Rc>, action: InputAction, role_id: String, role_type: Option, @@ -198,7 +200,7 @@ pub fn role_add_del( } pub fn task_add_del( - rconfig: &Rc>, + rconfig: &Rc>, role_id: String, action: InputAction, task_id: IdTask, @@ -271,10 +273,10 @@ pub fn task_add_del( } pub fn grant_revoke( - rconfig: &Rc>, + rconfig: &Rc>, role_id: String, action: InputAction, - mut actors: Vec, + mut actors: Vec, ) -> Result> { debug!("chsr role r1 grant|revoke"); let config = rconfig.as_ref().borrow_mut(); @@ -310,12 +312,12 @@ pub fn grant_revoke( } pub fn cred_set( - rconfig: &Rc>, + rconfig: &Rc>, role_id: String, task_id: IdTask, cred_caps: Option, - cred_setuid: Option, - cred_setgid: Option, + cred_setuid: Option, + cred_setgid: Option, ) -> Result> { debug!("chsr role r1 task t1 cred"); let config = rconfig.as_ref().borrow_mut(); @@ -337,12 +339,12 @@ pub fn cred_set( } pub fn cred_unset( - rconfig: &Rc>, + rconfig: &Rc>, role_id: String, task_id: IdTask, cred_caps: Option, - cred_setuid: Option, - cred_setgid: Option, + cred_setuid: Option, + cred_setgid: Option, ) -> Result> { debug!("chsr role r1 task t1 cred unset"); let config = rconfig.as_ref().borrow_mut(); @@ -370,7 +372,7 @@ pub fn cred_unset( } pub fn cred_caps( - rconfig: &Rc>, + rconfig: &Rc>, role_id: String, task_id: IdTask, setlist_type: SetListType, @@ -450,10 +452,10 @@ pub fn cred_caps( } pub fn cred_setpolicy( - rconfig: &Rc>, + rconfig: &Rc>, role_id: String, task_id: IdTask, - cred_policy: crate::common::database::structs::SetBehavior, + cred_policy: rar_common::database::structs::SetBehavior, ) -> Result> { debug!("chsr role r1 task t1 cred setpolicy"); let config = rconfig.as_ref().borrow_mut(); @@ -476,7 +478,7 @@ pub fn cred_setpolicy( } pub fn json_wildcard( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, action: InputAction, @@ -521,7 +523,7 @@ pub fn json_wildcard( } pub fn cmd_whitelist_action( - rconfig: &Rc>, + rconfig: &Rc>, role_id: String, task_id: IdTask, cmd_id: Vec, @@ -580,10 +582,10 @@ pub fn cmd_whitelist_action( } pub fn cmd_setpolicy( - rconfig: &Rc>, + rconfig: &Rc>, role_id: String, task_id: IdTask, - cmd_policy: crate::common::database::structs::SetBehavior, + cmd_policy: rar_common::database::structs::SetBehavior, ) -> Result> { debug!("chsr role r1 task t1 command setpolicy"); let config = rconfig.as_ref().borrow_mut(); @@ -597,7 +599,7 @@ pub fn cmd_setpolicy( } pub fn env_set_policylist( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, options_env: LinkedHashSet, @@ -623,10 +625,10 @@ pub fn env_set_policylist( } pub fn set_privileged( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, - options_root: crate::common::database::options::SPrivileged, + options_root: rar_common::database::options::SPrivileged, ) -> Result> { debug!("chsr o root set privileged"); perform_on_target_opt(rconfig, role_id, task_id, |opt: Rc>| { @@ -637,10 +639,10 @@ pub fn set_privileged( } pub fn set_bounding( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, - options_bounding: crate::common::database::options::SBounding, + options_bounding: rar_common::database::options::SBounding, ) -> Result> { debug!("chsr o bounding set"); perform_on_target_opt(rconfig, role_id, task_id, |opt: Rc>| { @@ -651,10 +653,10 @@ pub fn set_bounding( } pub fn set_authentication( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, - options_auth: crate::common::database::options::SAuthentication, + options_auth: rar_common::database::options::SAuthentication, ) -> Result> { debug!("chsr o auth set"); perform_on_target_opt(rconfig, role_id, task_id, |opt: Rc>| { @@ -665,7 +667,7 @@ pub fn set_authentication( } pub fn path_set( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, setlist_type: Option, @@ -695,7 +697,7 @@ pub fn path_set( } pub fn path_purge( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, setlist_type: Option, @@ -720,7 +722,7 @@ pub fn path_purge( } pub fn env_whitelist_set( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, setlist_type: Option, @@ -753,7 +755,7 @@ pub fn env_whitelist_set( } pub fn unset_timeout( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, timeout_arg: [bool; 3], @@ -782,10 +784,10 @@ pub fn unset_timeout( } pub fn set_timeout( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, - timeout_type: Option, + timeout_type: Option, timeout_duration: Option, timeout_max_usage: Option, ) -> Result> { @@ -808,7 +810,7 @@ pub fn set_timeout( } pub fn path_setlist2( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, setlist_type: Option, @@ -873,7 +875,7 @@ pub fn path_setlist2( } pub fn path_setpolicy( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, options_path_policy: PathBehavior, @@ -893,7 +895,7 @@ pub fn path_setpolicy( } pub fn env_setlist_add( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, setlist_type: Option, @@ -1026,7 +1028,7 @@ pub fn env_setlist_add( } pub fn env_setpolicy( - rconfig: &Rc>, + rconfig: &Rc>, role_id: Option, task_id: Option, options_env_policy: EnvBehavior, diff --git a/src/chsr/cli/usage.rs b/src/chsr/cli/usage.rs index 3af27183..47a20a3b 100644 --- a/src/chsr/cli/usage.rs +++ b/src/chsr/cli/usage.rs @@ -4,10 +4,8 @@ use const_format::formatcp; use tracing::debug; use super::data::Rule; -use crate::{ - common::util::{BOLD, RED, RST, UNDERLINE}, - util::underline, -}; +use crate::util::underline; +use rar_common::util::{BOLD, RED, RST, UNDERLINE}; const LONG_ABOUT: &str = "Role Manager is a tool to configure RBAC for RootAsRole. A role is a set of tasks that can be executed by a user or a group of users. diff --git a/src/chsr/main.rs b/src/chsr/main.rs index 660ba584..3c87eb33 100644 --- a/src/chsr/main.rs +++ b/src/chsr/main.rs @@ -1,30 +1,30 @@ //extern crate sudoers_reader; -use common::subsribe; -use common::{ - config::{self, Storage}, +use rar_common::{ + Storage, database::{read_json_config, save_json}, - drop_effective, + util::{ + drop_effective, read_effective, + subsribe, + }, plugin::register_plugins, - read_effective, + }; use tracing::{debug, error}; mod cli; -#[path = "../mod.rs"] -mod common; mod util; #[cfg(not(tarpaulin_include))] fn main() -> Result<(), Box> { - use common::config::ROOTASROLE; + use rar_common::{get_settings, StorageMethod, ROOTASROLE}; subsribe("chsr"); drop_effective()?; register_plugins(); - let settings = config::get_settings(ROOTASROLE).expect("Error on config read"); + let settings = get_settings(ROOTASROLE).expect("Error on config read"); let config = match settings.clone().as_ref().borrow().storage.method { - config::StorageMethod::JSON => Storage::JSON(read_json_config(settings.clone())?), + StorageMethod::JSON => Storage::JSON(read_json_config(settings.clone())?), _ => { error!("Unsupported storage method"); std::process::exit(1); diff --git a/src/chsr/util.rs b/src/chsr/util.rs index c9e1a87d..68e8521a 100644 --- a/src/chsr/util.rs +++ b/src/chsr/util.rs @@ -2,7 +2,7 @@ use std::mem; use pest::{error::LineColLocation, RuleType}; -use crate::common::util::escape_parser_string; +use rar_common::util::escape_parser_string; fn start(error: &pest::error::Error) -> (usize, usize) where diff --git a/src/mod.rs b/src/mod.rs deleted file mode 100644 index 4f0bca71..00000000 --- a/src/mod.rs +++ /dev/null @@ -1,167 +0,0 @@ -use capctl::{prctl, Cap, CapState}; -use serde::Serialize; -use std::{error::Error, ffi::CString, fs::File, path::Path}; -use tracing::{debug, Level}; -use tracing_subscriber::util::SubscriberInitExt; - -pub mod api; -pub mod config; -pub mod database; -pub mod util; -pub mod version; - -pub mod plugin; - -#[cfg(debug_assertions)] -pub fn subsribe(tool: &str) { - use std::io; - let identity = CString::new(tool).unwrap(); - let options = syslog_tracing::Options::LOG_PID; - let facility = syslog_tracing::Facility::Auth; - let _syslog = syslog_tracing::Syslog::new(identity, options, facility).unwrap(); - tracing_subscriber::fmt() - .with_max_level(Level::DEBUG) - .with_file(true) - .with_line_number(true) - .with_writer(io::stdout) - .finish() - .init(); -} - -#[cfg(not(debug_assertions))] -pub fn subsribe(tool: &str) { - use std::panic::set_hook; - - let identity = CString::new(tool).unwrap(); - let options = syslog_tracing::Options::LOG_PID; - let facility = syslog_tracing::Facility::Auth; - let syslog = syslog_tracing::Syslog::new(identity, options, facility).unwrap(); - tracing_subscriber::fmt() - .compact() - .with_max_level(Level::WARN) - .with_file(false) - .with_timer(false) - .with_line_number(false) - .with_target(false) - .without_time() - .with_writer(syslog) - .finish() - .init(); - set_hook(Box::new(|info| { - if let Some(s) = info.payload().downcast_ref::() { - println!("{}", s); - } - })); -} - -pub fn drop_effective() -> Result<(), capctl::Error> { - let mut current = CapState::get_current()?; - current.effective.clear(); - current.set_current() -} - -pub fn cap_effective(cap: Cap, enable: bool) -> Result<(), capctl::Error> { - let mut current = CapState::get_current()?; - current.effective.set_state(cap, enable); - current.set_current() -} - -pub fn setpcap_effective(enable: bool) -> Result<(), capctl::Error> { - cap_effective(Cap::SETPCAP, enable) -} - -pub fn setuid_effective(enable: bool) -> Result<(), capctl::Error> { - cap_effective(Cap::SETUID, enable) -} - -pub fn setgid_effective(enable: bool) -> Result<(), capctl::Error> { - cap_effective(Cap::SETGID, enable) -} - -pub fn fowner_effective(enable: bool) -> Result<(), capctl::Error> { - cap_effective(Cap::FOWNER, enable) -} - -pub fn read_effective(enable: bool) -> Result<(), capctl::Error> { - cap_effective(Cap::DAC_READ_SEARCH, enable) -} - -pub fn dac_override_effective(enable: bool) -> Result<(), capctl::Error> { - cap_effective(Cap::DAC_OVERRIDE, enable) -} - -pub fn immutable_effective(enable: bool) -> Result<(), capctl::Error> { - cap_effective(Cap::LINUX_IMMUTABLE, enable) -} - -pub fn activates_no_new_privs() -> Result<(), capctl::Error> { - prctl::set_no_new_privs() -} - -pub fn write_json_config(settings: &T, path: S) -> Result<(), Box> -where - S: std::convert::AsRef + Clone, -{ - let file = create_with_privileges(path)?; - serde_json::to_writer_pretty(file, &settings)?; - Ok(()) -} - -pub fn create_with_privileges>(p: P) -> Result { - std::fs::File::create(&p).or_else(|e| { - debug!( - "Error creating file without privilege, trying with privileges: {}", - e - ); - dac_override_effective(true)?; - let res = std::fs::File::create(p).inspect_err(|e| { - debug!( - "Error creating file without privilege, trying with privileges: {}", - e - ); - }); - dac_override_effective(false)?; - res - }) -} - -pub fn open_with_privileges>(p: P) -> Result { - std::fs::File::open(&p).or_else(|e| { - debug!( - "Error creating file without privilege, trying with privileges: {}", - e - ); - read_effective(true).or(dac_override_effective(true))?; - let res = std::fs::File::open(p); - read_effective(false)?; - dac_override_effective(false)?; - res - }) -} - -pub fn remove_with_privileges>(p: P) -> Result<(), std::io::Error> { - std::fs::remove_file(&p).or_else(|e| { - debug!( - "Error creating file without privilege, trying with privileges: {}", - e - ); - dac_override_effective(true)?; - let res = std::fs::remove_file(p); - dac_override_effective(false)?; - res - }) -} - -pub fn create_dir_all_with_privileges>(p: P) -> Result<(), std::io::Error> { - std::fs::create_dir_all(&p).or_else(|e| { - debug!( - "Error creating file without privilege, trying with privileges: {}", - e - ); - dac_override_effective(true)?; - let res = std::fs::create_dir_all(p); - read_effective(false)?; - dac_override_effective(false)?; - res - }) -} diff --git a/src/sr/main.rs b/src/sr/main.rs index bf2e34bd..908747a9 100644 --- a/src/sr/main.rs +++ b/src/sr/main.rs @@ -1,12 +1,10 @@ -#[path = "../mod.rs"] -mod common; pub mod pam; mod timeout; use capctl::CapState; -use common::database::finder::{Cred, FilterMatcher, TaskMatch, TaskMatcher}; -use common::database::{options::OptStack, structs::SConfig}; -use common::util::escape_parser_string; +use rar_common::database::finder::{Cred, FilterMatcher, TaskMatch, TaskMatcher}; +use rar_common::database::{options::OptStack, structs::SConfig}; +use rar_common::util::escape_parser_string; use const_format::formatcp; use nix::{ libc::dev_t, @@ -21,16 +19,13 @@ use std::panic::set_hook; use std::{cell::RefCell, error::Error, io::stdout, os::fd::AsRawFd, rc::Rc}; use tracing::{debug, error}; -use crate::common::plugin::register_plugins; -use crate::common::{ - activates_no_new_privs, - config::{self, Storage}, - dac_override_effective, +use rar_common::plugin::register_plugins; +use rar_common::{ + util::{dac_override_effective,activates_no_new_privs, setgid_effective, setpcap_effective, setuid_effective, + drop_effective, read_effective, subsribe, BOLD, RST, UNDERLINE}, + self, Storage, database::{read_json_config, structs::SGroups}, - read_effective, setgid_effective, setpcap_effective, setuid_effective, - util::{BOLD, RST, UNDERLINE}, }; -use crate::common::{drop_effective, subsribe}; //const ABOUT: &str = "Execute privileged commands with a role-based access control system"; //const LONG_ABOUT: &str = @@ -184,7 +179,7 @@ where #[cfg(not(tarpaulin_include))] fn main() -> Result<(), Box> { - use crate::{common::config::ROOTASROLE, pam::check_auth}; + use crate::{rar_common::ROOTASROLE, pam::check_auth}; subsribe("sr"); drop_effective()?; @@ -198,12 +193,12 @@ fn main() -> Result<(), Box> { read_effective(true) .or(dac_override_effective(true)) .unwrap_or_else(|_| panic!("{}", cap_effective_error("dac_read_search or dac_override"))); - let settings = config::get_settings(ROOTASROLE).expect("Failed to get settings"); + let settings = rar_common::get_settings(ROOTASROLE).expect("Failed to get settings"); read_effective(false) .and(dac_override_effective(false)) .unwrap_or_else(|_| panic!("{}", cap_effective_error("dac_read"))); let config = match settings.clone().as_ref().borrow().storage.method { - config::StorageMethod::JSON => { + rar_common::StorageMethod::JSON => { Storage::JSON(read_json_config(settings).expect("Failed to read config")) } _ => { @@ -326,7 +321,7 @@ fn make_cred() -> Cred { user } -fn set_capabilities(execcfg: &common::database::finder::ExecSettings, optstack: &OptStack) { +fn set_capabilities(execcfg: &rar_common::database::finder::ExecSettings, optstack: &OptStack) { //set capabilities if let Some(caps) = execcfg.caps { // case where capabilities are more than bounding set @@ -360,7 +355,7 @@ fn set_capabilities(execcfg: &common::database::finder::ExecSettings, optstack: } } -fn setuid_setgid(execcfg: &common::database::finder::ExecSettings) { +fn setuid_setgid(execcfg: &rar_common::database::finder::ExecSettings) { let uid = execcfg.setuid.as_ref().and_then(|u| { let res = u.into_user().unwrap_or(None); if let Some(user) = res { @@ -419,10 +414,11 @@ fn setuid_setgid(execcfg: &common::database::finder::ExecSettings) { mod tests { use libc::getgid; use nix::unistd::Pid; + use rar_common::rc_refcell; use super::*; - use crate::common::database::make_weak_config; - use crate::common::database::structs::{ + use rar_common::database::make_weak_config; + use rar_common::database::structs::{ IdTask, SActor, SCommand, SCommands, SConfig, SRole, STask, }; diff --git a/src/sr/pam/mod.rs b/src/sr/pam/mod.rs index bbb7f440..ff2daba4 100644 --- a/src/sr/pam/mod.rs +++ b/src/sr/pam/mod.rs @@ -8,13 +8,11 @@ use pam_client::{Context, ConversationHandler, ErrorCode, Flag}; use pcre2::bytes::RegexBuilder; use tracing::{debug, error, info, warn}; -use crate::{ - common::{ - config::Storage, - database::{finder::Cred, options::OptStack}, - }, - timeout, +use rar_common::{ + Storage, + database::{finder::Cred, options::OptStack}, }; +use crate::timeout; use self::rpassword::Terminal; diff --git a/src/sr/timeout.rs b/src/sr/timeout.rs index b31a6072..32e53c2b 100644 --- a/src/sr/timeout.rs +++ b/src/sr/timeout.rs @@ -15,13 +15,12 @@ use nix::{ use serde::{Deserialize, Serialize}; use tracing::debug; -use crate::common::{ - create_dir_all_with_privileges, create_with_privileges, +use rar_common::{ + util::{create_dir_all_with_privileges, create_with_privileges,open_with_privileges, remove_with_privileges}, database::{ finder::Cred, options::{STimeout, TimestampType}, }, - open_with_privileges, remove_with_privileges, }; /// This module checks the validity of a user's credentials diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 9a988a90..58682653 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -5,5 +5,8 @@ version = "3.0.0-alpha.5" edition = "2021" [dependencies] -anyhow = "1" -clap = { version = "4.1", features = ["derive"] } +rar-common = { path = "../rar-common" } +anyhow = "1.0.86" +clap = { version = "4.5.16", features = ["derive"] } +serde = { version = "1.0.209", features = ["rc"] } +serde_json = "1.0.127" diff --git a/xtask/src/install.rs b/xtask/src/install.rs new file mode 100644 index 00000000..b7bd568b --- /dev/null +++ b/xtask/src/install.rs @@ -0,0 +1,123 @@ +use std::fs::{self, File}; +use std::io::{self, BufRead, BufReader}; +use std::path::Path; + +use serde::{Deserialize, Serialize}; +use serde_json::Value; + + +pub fn post_install() -> Result<(), anyhow::Error> { + check_config_file()?; + check_filesystem()?; + + + Ok(()) +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct SettingsFile { + pub storage: Settings, + #[serde(default)] + #[serde(flatten, skip)] + pub _extra_fields: Value, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Settings { + #[serde(skip_serializing_if = "Option::is_none")] + pub settings: Option, + #[serde(default)] + #[serde(flatten)] + pub _extra_fields: Value, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RemoteStorageSettings { + #[serde(skip_serializing_if = "Option::is_none")] + pub immutable: Option, + #[serde(default)] + #[serde(flatten)] + pub _extra_fields: Value, +} + +const CONFIG_FILE: &str = "/etc/security/rootasrole.json"; + +fn check_filesystem() -> io::Result<()> { + let config = BufReader::new(File::open(CONFIG_FILE)?); + let mut config: SettingsFile = serde_json::from_reader(config)?; + // Get the filesystem type + if let Some(fs_type) = get_filesystem_type(CONFIG_FILE)? { + match fs_type.as_str() { + "ext2"|"ext3"|"ext4"|"xfs"|"btrfs"|"ocfs2"|"jfs"|"reiserfs" => { + set_immutable(&mut config, true); + } + _ => { + set_immutable(&mut config, false); + } + } + } else { + set_immutable(&mut config, false); + } + Ok(()) +} + +fn set_immutable(config: &mut SettingsFile, value: bool) { + if let Some(settings) = config.storage.settings.as_mut() { + if let Some(mut immutable) = settings.immutable { + immutable = value; + } + } +} + +fn get_filesystem_type>(path: P) -> io::Result> { + let path = path.as_ref(); + let mounts_file = File::open("/proc/mounts")?; + let reader = BufReader::new(mounts_file); + let mut longest_mount_point = String::new(); + let mut filesystem_type = None; + + for line_result in reader.lines() { + if let Ok(line_result) = line_result { + let fields: Vec<&str> = line_result.split_whitespace().collect(); + if fields.len() > 2 { + let mount_point = fields[1]; + let fs_type = fields[2]; + if path.starts_with(mount_point) && mount_point.len() > longest_mount_point.len() { + longest_mount_point = mount_point.to_string(); + filesystem_type = Some(fs_type.to_string()); + } + } + } else { + return Err(line_result.unwrap_err()); + } + } + + Ok(filesystem_type) +} + +fn check_config_file() -> io::Result<()> { + let default_path = "/usr/share/rootasrole/default.json"; + + // Check if the target file exists + if !Path::new(CONFIG_FILE).exists() { + // If the target file does not exist, copy the default file + if let Err(e) = fs::copy(default_path, CONFIG_FILE) { + eprintln!("Failed to copy the default configuration file to {}: {}", CONFIG_FILE, e); + std::process::exit(1); + } + } else { + // If the target file exists, compare it with the default file + if !files_are_equal(default_path, CONFIG_FILE)? { + std::process::exit(0); + } + } + + Ok(()) +} + +fn files_are_equal(path1: &str, path2: &str) -> io::Result { + let file1_content = fs::read(path1)?; + let file2_content = fs::read(path2)?; + + Ok(file1_content == file2_content) +} \ No newline at end of file diff --git a/xtask/src/main.rs b/xtask/src/main.rs index c1c594e0..b207ca45 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,5 +1,6 @@ mod build_ebpf; mod run; +mod install; use std::process::exit; @@ -15,6 +16,7 @@ pub struct Options { enum Command { BuildEbpf(build_ebpf::Options), Run(run::Options), + PostInstall, } fn main() { @@ -24,6 +26,7 @@ fn main() { let ret = match opts.command { BuildEbpf(opts) => build_ebpf::build_ebpf(opts), Run(opts) => run::run(opts), + PostInstall => install::post_install(), }; if let Err(e) = ret {