use std::collections::BTreeMap;
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::ExitStatus;
use crate::internal::app_adapter::{launcher, LaunchRequest, ResolutionStrategy, ResolvedProgram};
use crate::error::{Error, Result};
pub use crate::internal::app_adapter::IntegrationType;
pub struct SecureProcess {
program: PathBuf,
args: Vec<OsString>,
secret_env: BTreeMap<String, String>,
env_additions: BTreeMap<String, String>,
env_removals: Vec<String>,
scrub_patterns: Vec<String>,
}
impl std::fmt::Debug for SecureProcess {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SecureProcess")
.field("program", &self.program)
.field("args", &self.args)
.field("env_additions", &self.env_additions)
.field("env_removals", &self.env_removals)
.field("scrub_patterns", &self.scrub_patterns)
.finish()
}
}
impl SecureProcess {
pub fn new(program: impl Into<PathBuf>) -> Self {
Self {
program: program.into(),
args: Vec::new(),
secret_env: BTreeMap::new(),
env_additions: BTreeMap::new(),
env_removals: Vec::new(),
scrub_patterns: Vec::new(),
}
}
pub fn arg(mut self, a: impl Into<OsString>) -> Self {
self.args.push(a.into());
self
}
pub fn args(mut self, args: impl IntoIterator<Item = impl Into<OsString>>) -> Self {
self.args.extend(args.into_iter().map(Into::into));
self
}
pub fn secret_env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.secret_env.insert(key.into(), value.into());
self
}
pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.env_additions.insert(key.into(), value.into());
self
}
pub fn env_remove(mut self, key: impl Into<String>) -> Self {
self.env_removals.push(key.into());
self
}
pub fn scrub(mut self, pattern: impl Into<String>) -> Self {
self.scrub_patterns.push(pattern.into());
self
}
pub fn run(self) -> Result<ExitStatus> {
let mut env_overrides: BTreeMap<String, String> = BTreeMap::new();
for (k, v) in self.secret_env {
env_overrides.insert(k, v);
}
for (k, v) in self.env_additions {
env_overrides.insert(k, v);
}
let request = LaunchRequest {
program: ResolvedProgram {
path: self.program,
fixed_args: Vec::new(),
strategy: ResolutionStrategy::ExplicitPath,
shell_hint: None,
},
args: self
.args
.into_iter()
.map(|s| s.to_string_lossy().into_owned())
.collect(),
env_overrides,
env_removals: self.env_removals,
env_scrub_patterns: self.scrub_patterns,
};
launcher::run(request).map_err(|e| Error::KeyOperation {
operation: "exec".into(),
detail: e.to_string(),
})
}
#[allow(clippy::needless_return, unreachable_code)]
pub fn exec(self) -> Result<std::convert::Infallible> {
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
let mut cmd = std::process::Command::new(&self.program);
cmd.args(&self.args);
for (k, v) in &self.secret_env {
cmd.env(k, v);
}
for (k, v) in &self.env_additions {
cmd.env(k, v);
}
for k in &self.env_removals {
cmd.env_remove(k);
}
let err = cmd.exec();
return Err(Error::KeyOperation {
operation: "exec".into(),
detail: err.to_string(),
});
}
#[cfg(not(unix))]
{
let status = self.run()?;
let code = status.code().unwrap_or(1);
std::process::exit(code);
}
}
}
pub struct TempSecretFile {
#[cfg(target_os = "linux")]
_inner: TempSecretInner,
#[cfg(not(target_os = "linux"))]
_inner: crate::internal::app_adapter::TempConfig,
path_str: String,
}
#[cfg(target_os = "linux")]
#[allow(dead_code)]
enum TempSecretInner {
Memfd(crate::internal::app_adapter::MemfdConfig),
Fallback(crate::internal::app_adapter::TempConfig),
}
impl std::fmt::Debug for TempSecretFile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TempSecretFile")
.field("path", &self.path_str)
.finish()
}
}
impl TempSecretFile {
pub fn create(content: &str) -> Result<Self> {
Self::create_bytes(content.as_bytes())
}
pub fn create_bytes(content: &[u8]) -> Result<Self> {
#[cfg(target_os = "linux")]
{
match crate::internal::app_adapter::create_memfd_config(
"enclave-secret",
"secret",
content,
) {
Ok(memfd) => {
let path_str = memfd.path().to_string_lossy().into_owned();
Ok(Self {
_inner: TempSecretInner::Memfd(memfd),
path_str,
})
}
Err(_) => {
let tc = crate::internal::app_adapter::TempConfig::write(
"enclave-secret",
"secret",
content,
)
.map_err(|e| Error::KeyOperation {
operation: "temp_secret".into(),
detail: e.to_string(),
})?;
let path_str = tc.path().to_string_lossy().into_owned();
Ok(Self {
_inner: TempSecretInner::Fallback(tc),
path_str,
})
}
}
}
#[cfg(not(target_os = "linux"))]
{
let tc = crate::internal::app_adapter::TempConfig::write(
"enclave-secret",
"secret",
content,
)
.map_err(|e| Error::KeyOperation {
operation: "temp_secret".into(),
detail: e.to_string(),
})?;
let path_str = tc.path().to_string_lossy().into_owned();
Ok(Self {
_inner: tc,
path_str,
})
}
}
pub fn path(&self) -> &str {
&self.path_str
}
}