Improve selinux in tasks daemon (#1847)

This commit is contained in:
Firstyear 2023-07-11 15:39:28 +10:00 committed by GitHub
parent 6e01c4802f
commit 07580cf57a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 167 additions and 76 deletions

View file

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

View file

@ -35,7 +35,8 @@ async fn get_webdriver_client() -> fantoccini::Client {
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")
fantoccini::ClientBuilder::new(hyper_tls::HttpsConnector::new())
.connect("http://localhost:9515")
.await
.unwrap()
}
@ -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()
}
}

View file

@ -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<Labeler<File>, 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<File>,
) -> Result<(), String> {
fn do_setfscreatecon_for_path(path_raw: &String, labeler: &Labeler<File>) -> 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<Labeler<File>, 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<File>,
sel_lookup_path_raw: String,
},
}
impl SelinuxLabeler {
pub fn new(gid: u32, home_prefix: &str) -> Result<Self, String> {
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<P: AsRef<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())
}
}
}
}

View file

@ -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
let p = 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,
)?;
.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),
};