Skip to content

Commit

Permalink
Add IsoReader to cdrw module & use it to implement IsoSecretReader.
Browse files Browse the repository at this point in the history
  • Loading branch information
flihp committed Dec 13, 2024
1 parent 1189870 commit fff8f5e
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 33 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ x509-cert = "0.2.5"
yubihsm = { git = "https://github.com/oxidecomputer/yubihsm.rs", branch = "session-close", features = ["usb", "untested"] }
zeroize = "1.8.1"
zeroize_derive = "1.4.2"
glob = "0.3.1"
148 changes: 146 additions & 2 deletions src/cdrw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use anyhow::{anyhow, Context, Result};
use log::warn;
use std::{fs, path::Path, process::Command};
use log::{debug, warn};
use std::{
fs,
path::{Path, PathBuf},
process::Command,
};
use tempfile::{tempdir, TempDir};

pub struct IsoWriter {
Expand Down Expand Up @@ -58,3 +62,143 @@ impl IsoWriter {
Ok(())
}
}

pub struct IsoReader {
iso_file: PathBuf,
}

impl IsoReader {
pub fn new<P: AsRef<Path>>(iso: P) -> Self {
Self {
iso_file: PathBuf::from(iso.as_ref()),
}
}

pub fn read<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>> {
let loop_dev = loopback_setup(&self.iso_file)?;

let tmpdir = tempdir()?;
mount(&loop_dev, &tmpdir)?;

let src = tmpdir.path().join(&path);
let data = fs::read(src)?;

unmount(&tmpdir)?;

loopback_teardown(&loop_dev)?;

Ok(data)
}
}

// create loopback device for iso file and get the device path from
// losetup stdout
fn loopback_setup<P: AsRef<Path>>(iso_file: P) -> Result<String> {
let mut cmd = Command::new("losetup");
let output = cmd
.arg("-f")
.output()
.with_context(|| "unable to execute \"losetup\"")?;

debug!("executing command: \"{:#?}\"", cmd);

if !output.status.success() {
warn!("command failed with status: {}", output.status);
warn!("stderr: \"{}\"", String::from_utf8_lossy(&output.stderr));
return Err(anyhow!("Failed to get next available loopback device"));
}

let loop_dev = String::from(String::from_utf8(output.stdout)?.trim());
debug!("got loopback device: {}", loop_dev);

let mut cmd = Command::new("losetup");
let output = cmd
.arg(&loop_dev)
.arg(iso_file.as_ref())
.output()
.with_context(|| "failed to execute \"losetup\"")?;

debug!("executing command: \"{:#?}\"", cmd);
if output.status.success() {
Ok(loop_dev)
} else {
warn!("command failed with status: {}", output.status);
warn!("stderr: \"{}\"", String::from_utf8_lossy(&output.stderr));
Err(anyhow!(
"Failed to create loopback device {} for ISO file {}",
loop_dev,
iso_file.as_ref().display()
))
}
}

fn loopback_teardown<P: AsRef<Path>>(loop_dev: P) -> Result<()> {
// tear down the loopback device
let mut cmd = Command::new("losetup");
let output = cmd
.arg("-d")
.arg(loop_dev.as_ref())
.output()
.context("failed to execute \"losetup\"")?;

if output.status.success() {
Ok(())
} else {
warn!("command failed with status: {}", output.status);
warn!("stderr: \"{}\"", String::from_utf8_lossy(&output.stderr));
Err(anyhow!(
"Failed to delete loopback device {}",
loop_dev.as_ref().display()
))
}
}

// TODO: sys_mount crate
fn mount<P: AsRef<Path>, Q: AsRef<Path>>(
device: P,
mount_point: Q,
) -> Result<()> {
let mut cmd = Command::new("mount");
let output = cmd
.arg(device.as_ref())
.arg(mount_point.as_ref())
.output()
.with_context(|| {
format!(
"failed to mount \"{}\" at \"{}\"",
device.as_ref().display(),
mount_point.as_ref().display()
)
})?;

if output.status.success() {
Ok(())
} else {
warn!("command failed with status: {}", output.status);
warn!("stderr: \"{}\"", String::from_utf8_lossy(&output.stderr));
Err(anyhow!(
"Failed to mount device {} at {}",
device.as_ref().display(),
mount_point.as_ref().display()
))
}
}

fn unmount<P: AsRef<Path>>(mount_point: P) -> Result<()> {
// unmount now that we've got the data we need
let mut cmd = Command::new("umount");
let output = cmd.arg(mount_point.as_ref()).output().with_context(|| {
format!("failed to unmount \"{}\"", mount_point.as_ref().display())
})?;

if output.status.success() {
Ok(())
} else {
warn!("command failed with status: {}", output.status);
warn!("stderr: \"{}\"", String::from_utf8_lossy(&output.stderr));
Err(anyhow!(
"Failed to unmount at {}",
mount_point.as_ref().display()
))
}
}
34 changes: 19 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ use oks::{
ENV_NEW_PASSWORD, ENV_PASSWORD, KEYSPEC_EXT,
},
hsm::Hsm,
secret_reader::{self, PasswordReader, SecretInput, StdioPasswordReader},
secret_reader::{
self, PasswordReader, SecretInputArg, StdioPasswordReader,
},
secret_writer::{self, SecretOutputArg},
util,
};
Expand Down Expand Up @@ -72,8 +74,8 @@ struct Args {
#[derive(Subcommand, Debug, PartialEq)]
enum Command {
Ca {
#[clap(long, env)]
auth_method: SecretInput,
#[clap(flatten)]
auth_method: SecretInputArg,

#[command(subcommand)]
command: CaCommand,
Expand Down Expand Up @@ -156,8 +158,8 @@ enum CaCommand {
enum HsmCommand {
/// Generate keys in YubiHSM from specification.
Generate {
#[clap(long, env)]
auth_method: SecretInput,
#[clap(flatten)]
auth_method: SecretInputArg,

#[clap(long, env, default_value = "input")]
key_spec: PathBuf,
Expand All @@ -182,17 +184,17 @@ enum HsmCommand {
#[clap(long, env, default_value = "input")]
backups: PathBuf,

#[clap(long, env)]
share_method: SecretInput,
#[clap(flatten)]
share_method: SecretInputArg,

#[clap(long, env, default_value = "input/verifier.json")]
verifier: PathBuf,
},

/// Get serial number from YubiHSM and dump to console.
SerialNumber {
#[clap(long, env)]
auth_method: SecretInput,
#[clap(flatten)]
auth_method: SecretInputArg,
},
}

Expand Down Expand Up @@ -237,13 +239,14 @@ fn get_auth_id(auth_id: Option<Id>, command: &HsmCommand) -> Id {
/// the user with a password prompt.
fn get_passwd(
auth_id: Option<Id>,
auth_method: &SecretInput,
auth_method: &SecretInputArg,
command: &HsmCommand,
) -> Result<Zeroizing<String>> {
let passwd = match env::var(ENV_PASSWORD).ok() {
Some(s) => Zeroizing::new(s),
None => {
let passwd_reader = secret_reader::get_passwd_reader(*auth_method);
let mut passwd_reader =
secret_reader::get_passwd_reader(auth_method)?;

if auth_id.is_some() {
// if auth_id was set by the caller but not the password we
Expand Down Expand Up @@ -289,7 +292,7 @@ fn get_new_passwd(hsm: Option<&mut Hsm>) -> Result<Zeroizing<String>> {
}
// last option: challenge the caller
None => {
let passwd_reader = StdioPasswordReader::default();
let mut passwd_reader = StdioPasswordReader::default();
loop {
let password = passwd_reader.read(PASSWD_NEW)?;
let password2 = passwd_reader.read(PASSWD_NEW_2)?;
Expand Down Expand Up @@ -682,7 +685,8 @@ fn main() -> Result<()> {
} => {
// The CA modules pulls the password out of the environment. If
// ENV_PASSWORD isn't set the caller will be challenged.
let passwd_reader = secret_reader::get_passwd_reader(auth_method);
let mut passwd_reader =
secret_reader::get_passwd_reader(&auth_method)?;
let password = passwd_reader.read(PASSWD_PROMPT)?;
std::env::set_var(ENV_PASSWORD, password.deref());

Expand Down Expand Up @@ -826,9 +830,9 @@ fn main() -> Result<()> {
let verifier = fs::read_to_string(verifier)?;
let verifier: Verifier = serde_json::from_str(&verifier)?;
let share_itr = secret_reader::get_share_reader(
*share_method,
share_method,
verifier,
);
)?;

let mut shares: Zeroizing<Vec<Share>> =
Zeroizing::new(Vec::new());
Expand Down
Loading

0 comments on commit fff8f5e

Please sign in to comment.