diff --git a/book/src/DEVELOPER_README.md b/book/src/DEVELOPER_README.md index 3d87ca917..cc4fdf331 100644 --- a/book/src/DEVELOPER_README.md +++ b/book/src/DEVELOPER_README.md @@ -27,11 +27,35 @@ To build the Web UI you'll need [wasm-pack](https://rustwasm.github.io/wasm-pack You will need to install rustup and our build dependencies with: ```bash -zypper in rustup git libudev-devel sqlite3-devel libopenssl-3-devel +zypper in rustup git libudev-devel sqlite3-devel libopenssl-3-devel libselinux-devel pam-devel tpm2-0-tss-devel ``` You can then use rustup to complete the setup of the toolchain. +In some cases you may need to build other vendored components, or use an alternate linker. In these +cases we advise you to also install. + +```bash +zypper in clang lld make sccache +``` + +You should also adjust your environment with: +```bash +export RUSTC_WRAPPER=sccache +export CC="sccache /usr/bin/clang" +export CXX="sccache /usr/bin/clang++" +``` + +And add the following to a cargo config of your choice (such as ~/.cargo/config), adjusting for cpu arch + +```toml +[target.aarch64-unknown-linux-gnu] +linker = "clang" +rustflags = [ + "-C", "link-arg=-fuse-ld=lld", +] +``` + ### Fedora You will need [rustup](https://rustup.rs/) to install a Rust toolchain. diff --git a/server/testkit/tests/integration.rs b/server/testkit/tests/integration.rs index c4ff9a823..afb2541ae 100644 --- a/server/testkit/tests/integration.rs +++ b/server/testkit/tests/integration.rs @@ -28,18 +28,19 @@ async fn get_webdriver_client() -> fantoccini::Client { }; if !in_ci { match fantoccini::ClientBuilder::native() - .connect("http://localhost:4444") - .await - { - Ok(val) => val, - Err(_) => { - // trying the default chromedriver port - eprintln!("Couldn't connect on 4444, trying 9515"); - fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new()).connect("http://localhost:9515") - .await - .unwrap() - } - } + .connect("http://localhost:4444") + .await + { + Ok(val) => val, + Err(_) => { + // trying the default chromedriver port + eprintln!("Couldn't connect on 4444, trying 9515"); + fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new()) + .connect("http://localhost:9515") + .await + .unwrap() + } + } } else { println!("In CI setting headless and assuming Chrome"); let cap = json!({ @@ -48,8 +49,11 @@ async fn get_webdriver_client() -> fantoccini::Client { } }); let cap: Capabilities = serde_json::from_value(cap).unwrap(); - fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new()).capabilities(cap).connect("http://localhost:9515").await.unwrap() - + fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new()) + .capabilities(cap) + .connect("http://localhost:9515") + .await + .unwrap() } } diff --git a/unix_integration/src/selinux_util.rs b/unix_integration/src/selinux_util.rs index eb561553a..c56c3b71c 100644 --- a/unix_integration/src/selinux_util.rs +++ b/unix_integration/src/selinux_util.rs @@ -1,7 +1,11 @@ use std::ffi::CString; +use std::path::Path; +use std::process::Command; +use users::get_user_by_uid; use selinux::{ - current_mode, kernel_support, label::back_end::File, label::Labeler, KernelSupport, SELinuxMode, + current_mode, kernel_support, label::back_end::File, label::Labeler, KernelSupport, + SELinuxMode, SecurityContext, }; pub fn supported() -> bool { @@ -16,18 +20,7 @@ pub fn supported() -> bool { } } -pub fn get_labeler() -> Result, String> { - if let Ok(v) = Labeler::new(&[], true) { - Ok(v) - } else { - Err("Failed getting handle for SELinux labeling".to_string()) - } -} - -pub fn do_setfscreatecon_for_path( - path_raw: &String, - labeler: &Labeler, -) -> Result<(), String> { +fn do_setfscreatecon_for_path(path_raw: &String, labeler: &Labeler) -> Result<(), String> { match labeler.look_up(&CString::new(path_raw.to_owned()).unwrap(), 0) { Ok(context) => { if context.set_for_new_file_system_objects(true).is_err() { @@ -40,3 +33,98 @@ pub fn do_setfscreatecon_for_path( } } } + +fn get_labeler() -> Result, String> { + if let Ok(v) = Labeler::new(&[], true) { + Ok(v) + } else { + Err("Failed getting handle for SELinux labeling".to_string()) + } +} + +pub enum SelinuxLabeler { + None, + Enabled { + labeler: Labeler, + sel_lookup_path_raw: String, + }, +} + +impl SelinuxLabeler { + pub fn new(gid: u32, home_prefix: &str) -> Result { + let labeler = get_labeler()?; + + // Construct a path for SELinux context lookups. + // We do this because the policy only associates a home directory to its owning + // user by the name of the directory. Since the real user's home directory is (by + // default) their uuid or spn, its context will always be the policy default + // (usually user_u or unconfined_u). This lookup path is used to ask the policy + // what the context SHOULD be, and we will create policy equivalence rules below + // so that relabels in the future do not break it. + #[cfg(all(target_family = "unix", feature = "selinux"))] + // Yes, gid, because we use the GID number for both the user's UID and primary GID + let sel_lookup_path_raw = match get_user_by_uid(gid) { + Some(v) => format!("{}{}", home_prefix, v.name().to_str().unwrap()), + None => { + return Err("Failed looking up username by uid for SELinux relabeling".to_string()); + } + }; + + Ok(SelinuxLabeler::Enabled { + labeler, + sel_lookup_path_raw, + }) + } + + pub fn new_noop() -> Self { + SelinuxLabeler::None + } + + pub fn do_setfscreatecon_for_path(&self) -> Result<(), String> { + match &self { + SelinuxLabeler::None => Ok(()), + SelinuxLabeler::Enabled { + labeler, + sel_lookup_path_raw, + } => do_setfscreatecon_for_path(sel_lookup_path_raw, labeler), + } + } + + pub fn label_path>(&self, path: P) -> Result<(), String> { + match &self { + SelinuxLabeler::None => Ok(()), + SelinuxLabeler::Enabled { + labeler, + sel_lookup_path_raw, + } => { + let sel_lookup_path = Path::new(&sel_lookup_path_raw).join(path.as_ref()); + do_setfscreatecon_for_path(&sel_lookup_path.to_str().unwrap().to_string(), &labeler) + } + } + } + + pub fn setup_equivalence_rule(&self, path: &str) -> Result<(), String> { + match &self { + SelinuxLabeler::None => Ok(()), + SelinuxLabeler::Enabled { + labeler: _, + sel_lookup_path_raw, + } => Command::new("semanage") + .args(["fcontext", "-ae", sel_lookup_path_raw, &path]) + .spawn() + .map(|_| ()) + .map_err(|_| "Failed creating SELinux policy equivalence rule".to_string()), + } + } + + pub fn set_default_context_for_fs_objects(&self) -> Result<(), String> { + match &self { + SelinuxLabeler::None => Ok(()), + SelinuxLabeler::Enabled { .. } => { + SecurityContext::set_default_context_for_new_file_system_objects() + .map(|_| ()) + .map_err(|_| "Failed resetting SELinux file creation contexts".to_string()) + } + } + } +} diff --git a/unix_integration/src/tasks_daemon.rs b/unix_integration/src/tasks_daemon.rs index 19a7689a5..52ef96d98 100644 --- a/unix_integration/src/tasks_daemon.rs +++ b/unix_integration/src/tasks_daemon.rs @@ -36,12 +36,6 @@ use walkdir::WalkDir; #[cfg(all(target_family = "unix", feature = "selinux"))] use kanidm_unix_common::selinux_util; -#[cfg(all(target_family = "unix", feature = "selinux"))] -use selinux::SecurityContext; -#[cfg(all(target_family = "unix", feature = "selinux"))] -use std::process::Command; -#[cfg(all(target_family = "unix", feature = "selinux"))] -use users::get_user_by_uid; struct TaskCodec; @@ -96,6 +90,7 @@ fn create_home_directory( info: &HomeDirectoryInfo, home_prefix: &str, use_etc_skel: bool, + use_selinux: bool, ) -> Result<(), String> { // Final sanity check to prevent certain classes of attacks. let name = info.name.trim_start_matches('.').replace(['/', '\\'], ""); @@ -121,33 +116,22 @@ fn create_home_directory( } // Get a handle to the SELinux labeling interface + debug!(?use_selinux, "selinux for home dir labeling"); #[cfg(all(target_family = "unix", feature = "selinux"))] - let labeler = selinux_util::get_labeler()?; - - // Construct a path for SELinux context lookups. - // We do this because the policy only associates a home directory to its owning - // user by the name of the directory. Since the real user's home directory is (by - // default) their uuid or spn, its context will always be the policy default - // (usually user_u or unconfined_u). This lookup path is used to ask the policy - // what the context SHOULD be, and we will create policy equivalence rules below - // so that relabels in the future do not break it. - #[cfg(all(target_family = "unix", feature = "selinux"))] - // Yes, gid, because we use the GID number for both the user's UID and primary GID - let sel_lookup_path_raw = match get_user_by_uid(info.gid) { - Some(v) => format!("{}{}", home_prefix, v.name().to_str().unwrap()), - None => { - return Err("Failed looking up username by uid for SELinux relabeling".to_string()); - } + let labeler = if use_selinux { + selinux_util::SelinuxLabeler::new(info.gid, home_prefix)? + } else { + selinux_util::SelinuxLabeler::new_noop() }; // Does the home directory exist? if !hd_path.exists() { - // Set a umask - let before = unsafe { umask(0o0027) }; - // Set the SELinux security context for file creation #[cfg(all(target_family = "unix", feature = "selinux"))] - selinux_util::do_setfscreatecon_for_path(&sel_lookup_path_raw, &labeler)?; + labeler.do_setfscreatecon_for_path()?; + + // Set a umask + let before = unsafe { umask(0o0027) }; // Create the dir if let Err(e) = fs::create_dir_all(hd_path) { @@ -172,17 +156,11 @@ fn create_home_directory( #[cfg(all(target_family = "unix", feature = "selinux"))] { - // Look up the correct SELinux context of this object - let sel_lookup_path = Path::new(&sel_lookup_path_raw).join( - entry - .path() - .strip_prefix(skel_dir) - .map_err(|e| e.to_string())?, - ); - selinux_util::do_setfscreatecon_for_path( - &sel_lookup_path.to_str().unwrap().to_string(), - &labeler, - )?; + let p = entry + .path() + .strip_prefix(skel_dir) + .map_err(|e| e.to_string())?; + labeler.label_path(p)?; } if entry.path().is_dir() { @@ -194,22 +172,14 @@ fn create_home_directory( // Create equivalence rule in the SELinux policy #[cfg(all(target_family = "unix", feature = "selinux"))] - if Command::new("semanage") - .args(["fcontext", "-ae", &sel_lookup_path_raw, &hd_path_raw]) - .spawn() - .is_err() - { - return Err("Failed creating SELinux policy equivalence rule".to_string()); - } + labeler.setup_equivalence_rule(&hd_path_raw)?; } } } // Reset object creation SELinux context to default #[cfg(all(target_family = "unix", feature = "selinux"))] - if SecurityContext::set_default_context_for_new_file_system_objects().is_err() { - return Err("Failed resetting SELinux file creation contexts".to_string()); - } + labeler.set_default_context_for_fs_objects()?; let name_rel_path = Path::new(&name); // Does the aliases exist @@ -264,7 +234,12 @@ async fn handle_tasks(stream: UnixStream, cfg: &KanidmUnixdConfig) { Some(Ok(TaskRequest::HomeDirectory(info))) => { debug!("Received task -> HomeDirectory({:?})", info); - let resp = match create_home_directory(&info, &cfg.home_prefix, cfg.use_etc_skel) { + let resp = match create_home_directory( + &info, + &cfg.home_prefix, + cfg.use_etc_skel, + cfg.selinux, + ) { Ok(()) => TaskResponse::Success, Err(msg) => TaskResponse::Error(msg), };