diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2021-03-02 14:03:34 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2021-03-02 14:11:15 +0100 |
commit | a2e8337777242e2d97e4476581a70e2eec4f3cb9 (patch) | |
tree | 9d26bddf175a000bcabbea85ea28fc1a362ef697 | |
parent | d8016bacc2cfe59afd54d52dd0a654f0317c9630 (diff) |
sq: Refactor opening of output streams.
- Make the create_or_stdout* functions available as methods on the
Config struct. Adapt callsites.
- Also, differentiate between data that is safe to redirect to a
file or pipe to the next program (e.g. OpenPGP data, decrypted or
authenticated payloads) and data that could possibly be
scraped (e.g. packet dumps).
-rw-r--r-- | sq/src/commands/autocrypt.rs | 13 | ||||
-rw-r--r-- | sq/src/commands/certify.rs | 4 | ||||
-rw-r--r-- | sq/src/commands/key.rs | 19 | ||||
-rw-r--r-- | sq/src/commands/keyring.rs | 28 | ||||
-rw-r--r-- | sq/src/commands/mod.rs | 28 | ||||
-rw-r--r-- | sq/src/commands/net.rs | 11 | ||||
-rw-r--r-- | sq/src/commands/sign.rs | 42 | ||||
-rw-r--r-- | sq/src/sq.rs | 95 |
8 files changed, 130 insertions, 110 deletions
diff --git a/sq/src/commands/autocrypt.rs b/sq/src/commands/autocrypt.rs index b607c128..f498c5b7 100644 --- a/sq/src/commands/autocrypt.rs +++ b/sq/src/commands/autocrypt.rs @@ -10,8 +10,6 @@ use sequoia_autocrypt as autocrypt; use crate::{ Config, - create_or_stdout, - create_or_stdout_pgp, open_or_stdin, }; @@ -20,10 +18,9 @@ pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> { ("decode", Some(m)) => { let input = open_or_stdin(m.value_of("input"))?; let mut output = - create_or_stdout_pgp(m.value_of("output"), - config.force, - m.is_present("binary"), - armor::Kind::PublicKey)?; + config.create_or_stdout_pgp(m.value_of("output"), + m.is_present("binary"), + armor::Kind::PublicKey)?; let ac = autocrypt::AutocryptHeaders::from_reader(input)?; for h in &ac.headers { if let Some(ref cert) = h.key { @@ -34,8 +31,8 @@ pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> { }, ("encode-sender", Some(m)) => { let input = open_or_stdin(m.value_of("input"))?; - let mut output = create_or_stdout(m.value_of("output"), - config.force)?; + let mut output = + config.create_or_stdout_safe(m.value_of("output"))?; let cert = Cert::from_reader(input)?; let addr = m.value_of("address").map(|a| a.to_string()) .or_else(|| { diff --git a/sq/src/commands/certify.rs b/sq/src/commands/certify.rs index add05199..725fbc80 100644 --- a/sq/src/commands/certify.rs +++ b/sq/src/commands/certify.rs @@ -159,8 +159,8 @@ pub fn certify(config: Config, m: &clap::ArgMatches) // And export it. - let mut message = crate::create_or_stdout_pgp( - m.value_of("output"), config.force, + let mut message = config.create_or_stdout_pgp( + m.value_of("output"), m.is_present("binary"), sequoia_openpgp::armor::Kind::PublicKey)?; cert.serialize(&mut message)?; message.finalize()?; diff --git a/sq/src/commands/key.rs b/sq/src/commands/key.rs index 86891954..b48bffd3 100644 --- a/sq/src/commands/key.rs +++ b/sq/src/commands/key.rs @@ -20,14 +20,13 @@ use crate::{ open_or_stdin, }; use crate::Config; -use crate::create_or_stdout; use crate::SECONDS_IN_YEAR; use crate::parse_duration; use crate::decrypt_key; pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> { match m.subcommand() { - ("generate", Some(m)) => generate(m, config.force)?, + ("generate", Some(m)) => generate(config, m)?, ("extract-cert", Some(m)) => extract_cert(config, m)?, ("adopt", Some(m)) => adopt(config, m)?, ("attest-certifications", Some(m)) => @@ -37,7 +36,7 @@ pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> { Ok(()) } -fn generate(m: &ArgMatches, force: bool) -> Result<()> { +fn generate(config: Config, m: &ArgMatches) -> Result<()> { let mut builder = CertBuilder::new(); // User ID @@ -179,7 +178,7 @@ fn generate(m: &ArgMatches, force: bool) -> Result<()> { .map(|value| ("Comment", value.as_str())) .collect(); - let w = create_or_stdout(Some(&key_path), force)?; + let w = config.create_or_stdout_safe(Some(&key_path))?; let mut w = Writer::with_headers(w, Kind::SecretKey, headers)?; cert.as_tsk().serialize(&mut w)?; w.finalize()?; @@ -192,7 +191,7 @@ fn generate(m: &ArgMatches, force: bool) -> Result<()> { .collect(); headers.insert(0, ("Comment", "Revocation certificate for")); - let w = create_or_stdout(Some(&rev_path), force)?; + let w = config.create_or_stdout_safe(Some(&rev_path))?; let mut w = Writer::with_headers(w, Kind::Signature, headers)?; Packet::Signature(rev).serialize(&mut w)?; w.finalize()?; @@ -208,7 +207,7 @@ fn generate(m: &ArgMatches, force: bool) -> Result<()> { fn extract_cert(config: Config, m: &ArgMatches) -> Result<()> { let input = open_or_stdin(m.value_of("input"))?; - let mut output = create_or_stdout(m.value_of("output"), config.force)?; + let mut output = config.create_or_stdout_safe(m.value_of("output"))?; let cert = Cert::from_reader(input)?; if m.is_present("binary") { @@ -385,8 +384,8 @@ fn adopt(config: Config, m: &ArgMatches) -> Result<()> { let cert = cert.clone().insert_packets(packets.clone())?; - let mut message = crate::create_or_stdout_pgp( - m.value_of("output"), config.force, + let mut message = config.create_or_stdout_pgp( + m.value_of("output"), m.is_present("binary"), sequoia_openpgp::armor::Kind::SecretKey)?; cert.as_tsk().serialize(&mut message)?; message.finalize()?; @@ -560,8 +559,8 @@ fn attest_certifications(config: Config, m: &ArgMatches) // Finally, add the new signatures. let key = key.insert_packets(attestation_signatures)?; - let mut message = crate::create_or_stdout_pgp( - m.value_of("output"), config.force, m.is_present("binary"), + let mut message = config.create_or_stdout_pgp( + m.value_of("output"), m.is_present("binary"), sequoia_openpgp::armor::Kind::SecretKey)?; key.as_tsk().serialize(&mut message)?; message.finalize()?; diff --git a/sq/src/commands/keyring.rs b/sq/src/commands/keyring.rs index 2f30da75..c1d8ff87 100644 --- a/sq/src/commands/keyring.rs +++ b/sq/src/commands/keyring.rs @@ -26,11 +26,11 @@ use openpgp::{ }; use crate::{ + Config, open_or_stdin, - create_or_stdout_pgp, }; -pub fn dispatch(m: &clap::ArgMatches, force: bool) -> Result<()> { +pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> { match m.subcommand() { ("filter", Some(m)) => { let any_uid_predicates = @@ -114,10 +114,10 @@ pub fn dispatch(m: &clap::ArgMatches, force: bool) -> Result<()> { // better to use Kind::SecretKey here. However, this // requires buffering all certs, which has its own // problems. - let mut output = create_or_stdout_pgp(m.value_of("output"), - force, - m.is_present("binary"), - armor::Kind::PublicKey)?; + let mut output = + config.create_or_stdout_pgp(m.value_of("output"), + m.is_present("binary"), + armor::Kind::PublicKey)?; filter(m.values_of("input"), &mut output, filter_fn, to_certificate)?; output.finalize() @@ -128,18 +128,18 @@ pub fn dispatch(m: &clap::ArgMatches, force: bool) -> Result<()> { // better to use Kind::SecretKey here. However, this // requires buffering all certs, which has its own // problems. - let mut output = create_or_stdout_pgp(m.value_of("output"), - force, - m.is_present("binary"), - armor::Kind::PublicKey)?; + let mut output = + config.create_or_stdout_pgp(m.value_of("output"), + m.is_present("binary"), + armor::Kind::PublicKey)?; filter(m.values_of("input"), &mut output, |c| Some(c), false)?; output.finalize() }, ("merge", Some(m)) => { - let mut output = create_or_stdout_pgp(m.value_of("output"), - force, - m.is_present("binary"), - armor::Kind::PublicKey)?; + let mut output = + config.create_or_stdout_pgp(m.value_of("output"), + m.is_present("binary"), + armor::Kind::PublicKey)?; merge(m.values_of("input"), &mut output)?; output.finalize() }, diff --git a/sq/src/commands/mod.rs b/sq/src/commands/mod.rs index 8ae65b07..fd158ced 100644 --- a/sq/src/commands/mod.rs +++ b/sq/src/commands/mod.rs @@ -32,7 +32,6 @@ use crate::openpgp::policy::Policy; use crate::{ Config, parse_armor_kind, - create_or_stdout_pgp, }; #[cfg(feature = "autocrypt")] @@ -454,21 +453,22 @@ pub fn join(config: Config, m: &clap::ArgMatches) let mut sink = if m.is_present("binary") { // No need for any auto-detection. - Some(create_or_stdout_pgp(output, config.force, - true, // Binary. - armor::Kind::File)?) + Some(config.create_or_stdout_pgp(output, + true, // Binary. + armor::Kind::File)?) } else if let Some(kind) = kind { - Some(create_or_stdout_pgp(output, config.force, - false, // Armored. - kind)?) + Some(config.create_or_stdout_pgp(output, + false, // Armored. + kind)?) } else { None // Defer. }; /// Writes a bit-accurate copy of all top-level packets in PPR to /// OUTPUT. - fn copy(mut ppr: PacketParserResult, - output: Option<&str>, force: bool, + fn copy(config: &Config, + mut ppr: PacketParserResult, + output: Option<&str>, sink: &mut Option<Message>) -> Result<()> { while let PacketParserResult::Some(pp) = ppr { @@ -483,9 +483,9 @@ pub fn join(config: Config, m: &clap::ArgMatches) _ => armor::Kind::File, }; - *sink = Some(create_or_stdout_pgp(output, force, - false, // Armored. - kind)?); + *sink = Some(config.create_or_stdout_pgp(output, + false, // Armored. + kind)?); } // We (ab)use the mapping feature to create byte-accurate @@ -505,13 +505,13 @@ pub fn join(config: Config, m: &clap::ArgMatches) let ppr = openpgp::parse::PacketParserBuilder::from_file(name)? .map(true).build()?; - copy(ppr, output, config.force, &mut sink)?; + copy(&config, ppr, output, &mut sink)?; } } else { let ppr = openpgp::parse::PacketParserBuilder::from_reader(io::stdin())? .map(true).build()?; - copy(ppr, output, config.force, &mut sink)?; + copy(&config, ppr, output, &mut sink)?; } sink.unwrap().finalize()?; diff --git a/sq/src/commands/net.rs b/sq/src/commands/net.rs index 66805ee8..322ff8a0 100644 --- a/sq/src/commands/net.rs +++ b/sq/src/commands/net.rs @@ -27,7 +27,6 @@ use net::{ use crate::{ Config, open_or_stdin, - create_or_stdout, serialize_keyring, }; @@ -77,7 +76,7 @@ pub fn dispatch_keyserver(config: Config, m: &clap::ArgMatches) -> Result<()> { .context("Failed to retrieve cert")?; let mut output = - create_or_stdout(m.value_of("output"), config.force)?; + config.create_or_stdout_safe(m.value_of("output"))?; if ! m.is_present("binary") { cert.armored().serialize(&mut output) } else { @@ -87,8 +86,8 @@ pub fn dispatch_keyserver(config: Config, m: &clap::ArgMatches) -> Result<()> { let certs = rt.block_on(ks.search(addr)) .context("Failed to retrieve certs")?; - let mut output = create_or_stdout(m.value_of("output"), - config.force)?; + let mut output = + config.create_or_stdout_safe(m.value_of("output"))?; serialize_keyring(&mut output, &certs, m.is_present("binary"))?; } else { @@ -147,8 +146,8 @@ pub fn dispatch_wkd(config: Config, m: &clap::ArgMatches) -> Result<()> { // ``` // But to keep the parallelism with `store export` and `keyserver get`, // The output is armored if not `--binary` option is given. - let mut output = create_or_stdout(m.value_of("output"), - config.force)?; + let mut output = + config.create_or_stdout_safe(m.value_of("output"))?; serialize_keyring(&mut output, &certs, m.is_present("binary"))?; }, diff --git a/sq/src/commands/sign.rs b/sq/src/commands/sign.rs index 90ec39f0..6911ac65 100644 --- a/sq/src/commands/sign.rs +++ b/sq/src/commands/sign.rs @@ -18,38 +18,33 @@ use crate::openpgp::serialize::Serialize; use crate::openpgp::serialize::stream::{ Message, Armorer, Signer, LiteralWriter, }; -use crate::openpgp::policy::Policy; use crate::openpgp::types::SignatureType; use crate::{ Config, - create_or_stdout, - create_or_stdout_pgp, }; -pub fn sign(policy: &dyn Policy, +pub fn sign(config: Config, input: &mut (dyn io::Read + Sync + Send), output_path: Option<&str>, secrets: Vec<openpgp::Cert>, detached: bool, binary: bool, append: bool, notarize: bool, time: Option<SystemTime>, - notations: &[(bool, NotationData)], - force: bool) + notations: &[(bool, NotationData)]) -> Result<()> { match (detached, append|notarize) { (_, false) | (true, true) => - sign_data(policy, input, output_path, secrets, detached, binary, - append, time, notations, force), + sign_data(config, input, output_path, secrets, detached, binary, + append, time, notations), (false, true) => - sign_message(policy, input, output_path, secrets, binary, notarize, - time, notations, force), + sign_message(config, input, output_path, secrets, binary, notarize, + time, notations), } } -fn sign_data(policy: &dyn Policy, +fn sign_data(config: Config, input: &mut dyn io::Read, output_path: Option<&str>, secrets: Vec<openpgp::Cert>, detached: bool, binary: bool, append: bool, time: Option<SystemTime>, - notations: &[(bool, NotationData)], - force: bool) + notations: &[(bool, NotationData)]) -> Result<()> { let (mut output, prepend_sigs, tmp_path): (Box<dyn io::Write + Sync + Send>, Vec<Signature>, Option<PathBuf>) = @@ -81,10 +76,10 @@ fn sign_data(policy: &dyn Policy, let tmp_path = tmp_file.path().into(); (Box::new(tmp_file), sigs, Some(tmp_path)) } else { - (create_or_stdout(output_path, force)?, Vec::new(), None) + (config.create_or_stdout_safe(output_path)?, Vec::new(), None) }; - let mut keypairs = super::get_signing_keys(&secrets, policy, time)?; + let mut keypairs = super::get_signing_keys(&secrets, &config.policy, time)?; if keypairs.is_empty() { return Err(anyhow::anyhow!("No signing keys found")); } @@ -156,24 +151,23 @@ fn sign_data(policy: &dyn Policy, Ok(()) } -fn sign_message(policy: &dyn Policy, +fn sign_message(config: Config, input: &mut (dyn io::Read + Sync + Send), output_path: Option<&str>, secrets: Vec<openpgp::Cert>, binary: bool, notarize: bool, time: Option<SystemTime>, - notations: &[(bool, NotationData)], - force: bool) + notations: &[(bool, NotationData)]) -> Result<()> { let mut output = - create_or_stdout_pgp(output_path, force, - binary, - armor::Kind::Message)?; - sign_message_(policy, input, &mut output, secrets, notarize, time, notations)?; + config.create_or_stdout_pgp(output_path, + binary, + armor::Kind::Message)?; + sign_message_(config, input, &mut output, secrets, notarize, time, notations)?; output.finalize()?; Ok(()) } -fn sign_message_(policy: &dyn Policy, +fn sign_message_(config: Config, input: &mut (dyn io::Read + Sync + Send), output: &mut (dyn io::Write + Sync + Send), secrets: Vec<openpgp::Cert>, notarize: bool, @@ -181,7 +175,7 @@ fn sign_message_(policy: &dyn Policy, notations: &[(bool, NotationData)]) -> Result<()> { - let mut keypairs = super::get_signing_keys(&secrets, policy, time)?; + let mut keypairs = super::get_signing_keys(&secrets, &config.policy, time)?; if keypairs.is_empty() { return Err(anyhow::anyhow!("No signing keys found")); } diff --git a/sq/src/sq.rs b/sq/src/sq.rs index 9cb97205..7eaef9d3 100644 --- a/sq/src/sq.rs +++ b/sq/src/sq.rs @@ -38,6 +38,7 @@ fn open_or_stdin(f: Option<&str>) } } +#[deprecated(note = "Use the appropriate function on Config instead")] fn create_or_stdout(f: Option<&str>, force: bool) -> Result<Box<dyn io::Write + Sync + Send>> { match f { @@ -61,18 +62,6 @@ fn create_or_stdout(f: Option<&str>, force: bool) } } -fn create_or_stdout_pgp<'a>(f: Option<&str>, force: bool, - binary: bool, kind: armor::Kind) - -> Result<Message<'a>> -{ - let sink = create_or_stdout(f, force)?; - let mut message = Message::new(sink); - if ! binary { - message = Armorer::new(message).kind(kind).build()?; - } - Ok(message) -} - const SECONDS_IN_DAY : u64 = 24 * 60 * 60; const SECONDS_IN_YEAR : u64 = // Average number of days in a year. @@ -356,6 +345,43 @@ pub struct Config<'a> { policy: P<'a>, } +impl Config<'_> { + /// Opens the file (or stdout) for writing data that is safe for + /// non-interactive use. + /// + /// This is suitable for any kind of OpenPGP data, or decrypted or + /// authenticated payloads. + fn create_or_stdout_safe(&self, f: Option<&str>) + -> Result<Box<dyn io::Write + Sync + Send>> { + #[allow(deprecated)] + create_or_stdout(f, self.force) + } + + /// Opens the file (or stdout) for writing data that is NOT safe + /// for non-interactive use. + /// + /// If our heuristic detects non-interactive use, we will emit a + /// warning. + fn create_or_stdout_unsafe(&self, f: Option<&str>) + -> Result<Box<dyn io::Write + Sync + Send>> { + #[allow(deprecated)] + create_or_stdout(f, self.force) + } + + /// Opens the file (or stdout) for writing data that is safe for + /// non-interactive use because it is an OpenPGP data stream. + fn create_or_stdout_pgp<'a>(&self, f: Option<&str>, + binary: bool, kind: armor::Kind) + -> Result<Message<'a>> { + let sink = self.create_or_stdout_safe(f)?; + let mut message = Message::new(sink); + if ! binary { + message = Armorer::new(message).kind(kind).build()?; + } + Ok(message) + } +} + fn main() -> Result<()> { let policy = &mut P::new(); @@ -382,7 +408,8 @@ fn main() -> Result<()> { match matches.subcommand() { ("decrypt", Some(m)) => { let mut input = open_or_stdin(m.value_of("input"))?; - let mut output = create_or_stdout(m.value_of("output"), force)?; + let mut output = + config.create_or_stdout_safe(m.value_of("output"))?; let certs = m.values_of("sender-cert-file") .map(load_certs) .unwrap_or(Ok(vec![]))?; @@ -416,9 +443,9 @@ fn main() -> Result<()> { .unwrap_or(Ok(vec![]))?; let mut input = open_or_stdin(m.value_of("input"))?; let output = - create_or_stdout_pgp(m.value_of("output"), force, - m.is_present("binary"), - armor::Kind::Message)?; + config.create_or_stdout_pgp(m.value_of("output"), + m.is_present("binary"), + armor::Kind::Message)?; let additional_secrets = m.values_of("signer-key-file") .map(load_keys) .unwrap_or(Ok(vec![]))?; @@ -489,22 +516,23 @@ fn main() -> Result<()> { } if let Some(merge) = m.value_of("merge") { - let output = create_or_stdout_pgp(output, force, binary, - armor::Kind::Message)?; + let output = config.create_or_stdout_pgp(output, binary, + armor::Kind::Message)?; let mut input2 = open_or_stdin(Some(merge))?; commands::merge_signatures(&mut input, &mut input2, output)?; } else if m.is_present("clearsign") { - let output = create_or_stdout(output, force)?; + let output = config.create_or_stdout_safe(output)?; commands::sign::clearsign(config, input, output, secrets, time, ¬ations)?; } else { - commands::sign(policy, &mut input, output, secrets, detached, - binary, append, notarize, time, ¬ations, force)?; + commands::sign(config, &mut input, output, secrets, detached, + binary, append, notarize, time, ¬ations)?; } }, ("verify", Some(m)) => { let mut input = open_or_stdin(m.value_of("input"))?; - let mut output = create_or_stdout(m.value_of("output"), force)?; + let mut output = + config.create_or_stdout_safe(m.value_of("output"))?; let mut detached = if let Some(f) = m.value_of("detached") { Some(File::open(f)?) } else { @@ -541,7 +569,7 @@ fn main() -> Result<()> { { // It is already armored and has the correct kind. let mut output = - create_or_stdout(m.value_of("output"), force)?; + config.create_or_stdout_safe(m.value_of("output"))?; io::copy(&mut input, &mut output)?; return Ok(()); } @@ -556,8 +584,8 @@ fn main() -> Result<()> { let want_kind = want_kind.expect("given or detected"); let mut output = - create_or_stdout_pgp(m.value_of("output"), force, - false, want_kind)?; + config.create_or_stdout_pgp(m.value_of("output"), + false, want_kind)?; if already_armored { // Dearmor and copy to change the type. @@ -572,7 +600,8 @@ fn main() -> Result<()> { }, ("dearmor", Some(m)) => { let mut input = open_or_stdin(m.value_of("input"))?; - let mut output = create_or_stdout(m.value_of("output"), force)?; + let mut output = + config.create_or_stdout_safe(m.value_of("output"))?; let mut filter = armor::Reader::new(&mut input, None); io::copy(&mut filter, &mut output)?; }, @@ -580,16 +609,18 @@ fn main() -> Result<()> { ("autocrypt", Some(m)) => commands::autocrypt::dispatch(config, m)?, ("inspect", Some(m)) => { - let mut output = create_or_stdout(m.value_of("output"), force)?; + let mut output = + config.create_or_stdout_unsafe(m.value_of("output"))?; commands::inspect(m, policy, &mut output)?; }, - ("keyring", Some(m)) => commands::keyring::dispatch(m, force)?, + ("keyring", Some(m)) => commands::keyring::dispatch(config, m)?, ("packet", Some(m)) => match m.subcommand() { ("dump", Some(m)) => { let mut input = open_or_stdin(m.value_of("input"))?; - let mut output = create_or_stdout(m.value_of("output"), force)?; + let mut output = + config.create_or_stdout_unsafe(m.value_of("output"))?; let session_key: Option<openpgp::crypto::SessionKey> = if let Some(sk) = m.value_of("session-key") { Some(hex::decode_pretty(sk)?.into()) @@ -605,9 +636,9 @@ fn main() -> Result<()> { ("decrypt", Some(m)) => { let mut input = open_or_stdin(m.value_of("input"))?; let mut output = - create_or_stdout_pgp(m.value_of("output"), force, - m.is_present("binary"), - armor::Kind::Message)?; + config.create_or_stdout_pgp(m.value_of("output"), + m.is_present("binary"), + armor::Kind::Message)?; let secrets = m.values_of("secret-key-file") .map(load_keys) .unwrap_or(Ok(vec![]))?; |