diff options
-rw-r--r-- | core/src/lib.rs | 114 | ||||
-rw-r--r-- | ffi/src/lib.rs | 29 | ||||
-rw-r--r-- | ffi/src/sequoia.h | 32 | ||||
-rw-r--r-- | net/src/lib.rs | 24 |
4 files changed, 192 insertions, 7 deletions
diff --git a/core/src/lib.rs b/core/src/lib.rs index 12085a11..1ac45c33 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -30,6 +30,7 @@ pub struct Context { domain: String, home: PathBuf, lib: PathBuf, + network_policy: NetworkPolicy, } /// Returns $PREXIX, or a reasonable default prefix. @@ -63,6 +64,7 @@ impl Context { home: env::home_dir().unwrap_or(env::temp_dir()) .join(".sequoia"), lib: prefix().join("lib").join("sequoia"), + network_policy: NetworkPolicy::Encrypted, }) } @@ -80,6 +82,12 @@ impl Context { pub fn lib(&self) -> &Path { &self.lib } + + /// Returns the network policy. + pub fn network_policy(&self) -> &NetworkPolicy { + &self.network_policy + } + } /// Represents a `Context` configuration. @@ -124,6 +132,17 @@ impl Config { pub fn set_lib<P: AsRef<Path>>(&mut self, lib: P) { self.0.lib = PathBuf::new().join(lib); } + + /// Sets the network policy. + pub fn network_policy(mut self, policy: NetworkPolicy) -> Self { + self.set_network_policy(policy); + self + } + + /// Sets the network policy. + pub fn set_network_policy(&mut self, policy: NetworkPolicy) { + self.0.network_policy = policy; + } } /* Error handling. */ @@ -134,6 +153,8 @@ pub type Result<T> = ::std::result::Result<T, Error>; /// Errors for Sequoia. #[derive(Debug)] pub enum Error { + /// The network policy was violated by the given action. + NetworkPolicyViolation(NetworkPolicy), /// An `io::Error` occured. IoError(io::Error), } @@ -143,3 +164,96 @@ impl From<io::Error> for Error { Error::IoError(error) } } + +/* Network policy. */ + +/// Network policy for Sequoia. +/// +/// With this policy you can control how Sequoia accesses remote +/// systems. +#[derive(PartialEq, PartialOrd, Debug, Copy, Clone)] +pub enum NetworkPolicy { + /// Do not contact remote systems. + Offline, + + /// Only contact remote systems using anonymization techniques + /// like TOR. + Anonymized, + + /// Only contact remote systems using transports offering + /// encryption and authentication like TLS. + Encrypted, + + /// Contact remote systems even with insecure transports. + Insecure, +} + +impl NetworkPolicy { + pub fn assert(&self, action: NetworkPolicy) -> Result<()> { + if action > *self { + Err(Error::NetworkPolicyViolation(action)) + } else { + Ok(()) + } + } +} + +#[cfg(test)] +mod network_policy_test { + use super::{Error, NetworkPolicy}; + + macro_rules! assert_match { + ( $result:expr, $error: pat ) => { + if let $error = $result { + /* Pass. */ + } else { + panic!("Expected {}, got {:?}.", stringify!($error), $result); + } + }; + } + + + #[test] + fn offline() { + let p = NetworkPolicy::Offline; + assert_match!(p.assert(NetworkPolicy::Anonymized), + Err(Error::NetworkPolicyViolation(_))); + assert_match!(p.assert(NetworkPolicy::Encrypted), + Err(Error::NetworkPolicyViolation(_))); + assert_match!(p.assert(NetworkPolicy::Insecure), + Err(Error::NetworkPolicyViolation(_))); + } + + #[test] + fn anonymized() { + let p = NetworkPolicy::Anonymized; + assert_match!(p.assert(NetworkPolicy::Anonymized), + Ok(())); + assert_match!(p.assert(NetworkPolicy::Encrypted), + Err(Error::NetworkPolicyViolation(_))); + assert_match!(p.assert(NetworkPolicy::Insecure), + Err(Error::NetworkPolicyViolation(_))); + } + + #[test] + fn encrypted() { + let p = NetworkPolicy::Encrypted; + assert_match!(p.assert(NetworkPolicy::Anonymized), + Ok(())); + assert_match!(p.assert(NetworkPolicy::Encrypted), + Ok(())); + assert_match!(p.assert(NetworkPolicy::Insecure), + Err(Error::NetworkPolicyViolation(_))); + } + + #[test] + fn insecure() { + let p = NetworkPolicy::Insecure; + assert_match!(p.assert(NetworkPolicy::Anonymized), + Ok(())); + assert_match!(p.assert(NetworkPolicy::Encrypted), + Ok(())); + assert_match!(p.assert(NetworkPolicy::Insecure), + Ok(())); + } +} diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index b4f20eee..7ba09b10 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -42,7 +42,7 @@ use openpgp::tpk::TPK; use openpgp::types::KeyId; use self::libc::{uint8_t, uint64_t, c_char, size_t}; use self::native_tls::Certificate; -use sequoia_core::{Config, Context}; +use sequoia_core::{Config, Context, NetworkPolicy}; use sequoia_net::KeyServer; /* sequoia::Context. */ @@ -118,6 +118,19 @@ pub extern "system" fn sq_context_lib(ctx: Option<&Context>) -> *const c_char { ctx.unwrap().lib().to_string_lossy().as_bytes().as_ptr() as *const c_char } +/// Returns the network policy. +#[no_mangle] +pub extern "system" fn sq_context_network_policy(ctx: Option<&Context>) -> uint8_t { + assert!(ctx.is_some()); + match ctx.unwrap().network_policy() { + &NetworkPolicy::Offline => 0, + &NetworkPolicy::Anonymized => 1, + &NetworkPolicy::Encrypted => 2, + &NetworkPolicy::Insecure => 3, + } +} + + /* sequoia::Config. */ /// Finalizes the configuration and return a `Context`. @@ -160,6 +173,20 @@ pub extern "system" fn sq_config_lib(cfg: Option<&mut Config>, cfg.unwrap().set_lib(&lib.as_ref()) } +/// Sets the network policy. +#[no_mangle] +pub extern "system" fn sq_config_network_policy(cfg: Option<&mut Config>, + policy: uint8_t) { + assert!(cfg.is_some()); + cfg.unwrap().set_network_policy(match policy { + 0 => NetworkPolicy::Offline, + 1 => NetworkPolicy::Anonymized, + 2 => NetworkPolicy::Encrypted, + 3 => NetworkPolicy::Insecure, + n => panic!("Bad policy: {}", n), + }); +} + /* openpgp::types. */ /// Returns a KeyID with the given `id`. diff --git a/ffi/src/sequoia.h b/ffi/src/sequoia.h index c3b3db04..c19db967 100644 --- a/ffi/src/sequoia.h +++ b/ffi/src/sequoia.h @@ -25,6 +25,27 @@ struct sq_context; struct sq_config; /*/ +/// Network policy for Sequoia. +/// +/// With this policy you can control how Sequoia accesses remote +/// systems. +/*/ + +/* Do not contact remote systems. */ +#define SQ_NETWORK_POLICY_OFFLINE 0 + +/* Only contact remote systems using anonymization techniques + * like TOR. */ +#define SQ_NETWORK_POLICY_ANONYMIZED 1 + +/* Only contact remote systems using transports offering + * encryption and authentication like TLS. */ +#define SQ_NETWORK_POLICY_ENCRYPTED 2 + +/* Contact remote systems even with insecure transports. */ +#define SQ_NETWORK_POLICY_INSECURE 3 + +/*/ /// Creates a Context with reasonable defaults. /// /// `domain` should uniquely identify your application, it is strongly @@ -68,6 +89,11 @@ const char *sq_context_home(const struct sq_context *ctx); /*/ const char *sq_context_lib(const struct sq_context *ctx); +/*/ +/// Returns the network policy. +/*/ +uint8_t sq_context_network_policy(const struct sq_context *ctx); + /* sequoia::Config. */ @@ -88,6 +114,12 @@ void sq_config_home(struct sq_config *cfg, const char *home); /*/ void sq_config_lib(struct sq_config *cfg, const char *lib); +/*/ +/// Sets the network policy. +/*/ +void sq_config_network_policy(struct sq_config *cfg, uint8_t policy); + + /* sequoia::openpgp::types. */ /*/ diff --git a/net/src/lib.rs b/net/src/lib.rs index 36b822c1..b0e10a71 100644 --- a/net/src/lib.rs +++ b/net/src/lib.rs @@ -51,7 +51,7 @@ use std::convert::From; use std::io::{Cursor, Read}; use std::io; -use sequoia_core::Context; +use sequoia_core::{Context, NetworkPolicy}; use openpgp::tpk::{self, TPK}; use openpgp::types::KeyId; use openpgp::{Message, armor}; @@ -128,9 +128,14 @@ impl KeyServer { } /// Common code for the above functions. - fn make(_ctx: &Context, core: Core, client: Box<AClient>, uri: Uri) -> Result<Self> { - let uri = { - let s = uri.scheme().ok_or(Error::MalformedUri)?; + fn make(ctx: &Context, core: Core, client: Box<AClient>, uri: Uri) -> Result<Self> { + let s = uri.scheme().ok_or(Error::MalformedUri)?; + match s { + "hkp" => ctx.network_policy().assert(NetworkPolicy::Insecure), + "hkps" => ctx.network_policy().assert(NetworkPolicy::Encrypted), + _ => unreachable!() + }?; + let uri = format!("{}://{}:{}", match s {"hkp" => "http", "hkps" => "https", _ => unreachable!()}, uri.host().ok_or(Error::MalformedUri)?, @@ -138,8 +143,7 @@ impl KeyServer { "hkp" => uri.port().or(Some(11371)), "hkps" => uri.port().or(Some(443)), _ => unreachable!(), - }.unwrap()) - }.parse()?; + }.unwrap()).parse()?; Ok(KeyServer{core: core, client: client, uri: uri}) } @@ -248,6 +252,8 @@ pub enum Error { ProtocolViolation, /// There was an error parsing the key. KeysError(tpk::Error), + /// A `sequoia_core::Error` occured. + CoreError(sequoia_core::Error), /// Encountered an unexpected low-level http status. HttpStatus(hyper::StatusCode), /// An `io::Error` occured. @@ -266,6 +272,12 @@ impl From<tpk::Error> for Error { } } +impl From<sequoia_core::Error> for Error { + fn from(e: sequoia_core::Error) -> Self { + Error::CoreError(e) + } +} + impl From<hyper::StatusCode> for Error { fn from(status: hyper::StatusCode) -> Self { Error::HttpStatus(status) |