Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tighten permissions of ROBOCORP_HOME base (Windows only) #647

Merged
merged 2 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/bin/scheduler/setup/steps/directories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::internal_config::{GlobalConfig, Plan, Source};
#[cfg(windows)]
use crate::setup::ownership::transfer_directory_ownership_recursive;
#[cfg(windows)]
use crate::setup::windows_permissions::{grant_full_access, reset_access};
use crate::setup::windows_permissions::{grant_full_access, reset_access, run_icacls_command};

use camino::Utf8PathBuf;
use robotmk::environment::Environment;
Expand Down Expand Up @@ -78,6 +78,25 @@ impl SetupStep for StepRobocorpHomeBase {
err,
)
})?;
run_icacls_command([self.target.as_str(), "/inheritancelevel:r"]).map_err(|err| {
api::Error::new(
format!(
"Failed to set remove permission inheritance for {}",
self.target
),
err,
)
})?;
grant_full_access(
"*S-1-5-32-544", // Administrators (https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers)
&self.target,
)
.map_err(|err| {
api::Error::new(
format!("Failed to set permissions for {}", self.target),
err,
)
})?;
Ok(())
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/bin/scheduler/setup/windows_permissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ pub fn run_icacls_command<'a>(arguments: impl IntoIterator<Item = &'a str>) -> a
run_command("icacls.exe", arguments)
}

pub fn grant_full_access(user: &str, target_path: &Utf8Path) -> anyhow::Result<()> {
pub fn grant_full_access(sid: &str, target_path: &Utf8Path) -> anyhow::Result<()> {
let arguments = [
target_path.as_ref(),
"/grant",
&format!("{user}:(OI)(CI)F"),
&format!("{sid}:(OI)(CI)F"),
"/T",
];
run_icacls_command(arguments).map_err(|e| {
let message = format!("Adjusting permissions of {target_path} for user `{user}` failed");
let message = format!("Adjusting permissions of {target_path} for SID `{sid}` failed");
e.context(message)
})
}
Expand Down
67 changes: 63 additions & 4 deletions tests/test_scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ use serde_json::to_string;
use std::ffi::OsStr;
use std::fs::{create_dir_all, remove_file, write};
use std::path::Path;
#[cfg(windows)]
use std::process::Output;
use std::time::Duration;
use tokio::{
process::Command,
Expand Down Expand Up @@ -115,6 +117,12 @@ async fn test_scheduler() -> AnyhowResult<()> {
.join("plans")
.join(&config.plan_groups[0].plans[1].id),
);
assert_robocorp_home(
&config.rcc_config.robocorp_home_base,
#[cfg(windows)]
&current_user_name,
)
.await?;

Ok(())
}
Expand Down Expand Up @@ -518,11 +526,19 @@ async fn assert_working_directory(
}

#[cfg(windows)]
async fn get_permissions(path: impl AsRef<OsStr>) -> AnyhowResult<String> {
async fn run_icacls<I, S>(args: I) -> std::io::Result<Output>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut icacls_command = Command::new("icacls.exe");
icacls_command.arg(path);
let permissions = String::from_utf8(icacls_command.output().await?.stdout)?;
Ok(permissions)
icacls_command.args(args);
icacls_command.output().await
}

#[cfg(windows)]
async fn get_permissions(path: impl AsRef<OsStr>) -> AnyhowResult<String> {
Ok(String::from_utf8(run_icacls(&[path]).await?.stdout)?)
}

#[cfg(windows)]
Expand Down Expand Up @@ -660,3 +676,46 @@ fn assert_sequentiality(
dir_entries_second_plan.sort();
assert!(dir_entries_first_plan[0] < dir_entries_second_plan[0]);
}

async fn assert_robocorp_home(
robocorp_home_base: &Utf8Path,
#[cfg(windows)] headed_user_name: &str,
) -> AnyhowResult<()> {
assert!(robocorp_home_base.is_dir());
assert_eq!(
directory_entries(robocorp_home_base, 1),
[
"current_user",
#[cfg(windows)]
&format!("user_{headed_user_name}"),
]
);
#[cfg(windows)]
{
let permissions_robocorp_home_base = get_permissions(robocorp_home_base).await?;
assert_eq!(
jherbel marked this conversation as resolved.
Show resolved Hide resolved
permissions_robocorp_home_base
.lines()
.collect::<Vec<&str>>()
.len(),
3 // Administrator group + empty line + success message (suppressing the latter with /q does not seem to work)
);
assert!(
run_icacls([robocorp_home_base.as_str(), "/findsid", "*S-1-5-32-544"])
.await?
.status
.success()
);
assert!(
get_permissions(robocorp_home_base.join(format!("user_{headed_user_name}")))
.await?
.contains(&format!("{headed_user_name}:(OI)(CI)(F)",))
);
assert!(
get_permissions(robocorp_home_base.join(format!("user_{headed_user_name}")))
.await?
.contains(&format!("{headed_user_name}:(OI)(CI)(F)",))
);
}
Ok(())
}