use deelevate::{BridgeServer, Command, PrivilegeLevel, Token};
use pathsearch::find_executable_in_path;
use std::ffi::OsString;
use structopt::*;
#[derive(StructOpt)]
#[structopt(
about = "NormDo - \"Do\" a command with Normal privileges",
author = "Wez Furlong",
setting(clap::AppSettings::TrailingVarArg),
setting(clap::AppSettings::ArgRequiredElseHelp),
version = env!("VERGEN_SEMVER_LIGHTWEIGHT")
)]
#[derive(Debug)]
struct Opt {
#[structopt(value_name("PROGRAM"), parse(from_os_str))]
args: Vec<OsString>,
}
fn main() -> std::io::Result<()> {
let mut opt = Opt::from_args();
let token = Token::with_current_process()?;
let level = token.privilege_level()?;
opt.args[0] = match find_executable_in_path(&opt.args[0]) {
Some(path) => path.into(),
None => {
eprintln!("Unable to find {:?} in path", opt.args[0]);
std::process::exit(1);
}
};
let target_token = match level {
PrivilegeLevel::NotPrivileged => token,
PrivilegeLevel::HighIntegrityAdmin => token.as_medium_integrity_safer_token()?,
PrivilegeLevel::Elevated => Token::with_shell_process()?,
};
let mut command = Command::with_environment_for_token(&target_token)?;
let exit_code = match level {
PrivilegeLevel::NotPrivileged => {
command.set_argv(opt.args);
let proc = command.spawn()?;
let _ = proc.wait_for(None);
proc.exit_code()?
}
PrivilegeLevel::HighIntegrityAdmin | PrivilegeLevel::Elevated => {
let mut server = BridgeServer::new();
let mut bridge_cmd = server.start_for_command(&mut opt.args, &target_token)?;
let proc = match level {
PrivilegeLevel::Elevated => bridge_cmd.spawn_with_token(&target_token)?,
PrivilegeLevel::NotPrivileged | PrivilegeLevel::HighIntegrityAdmin => {
bridge_cmd.spawn_as_user(&target_token)?
}
};
server.serve(proc)?
}
};
std::process::exit(exit_code as _);
}