diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2018-06-13 17:24:07 +0200 |
|---|---|---|
| committer | Emīls Piņķis <emils@mullvad.net> | 2018-07-05 16:05:05 +0100 |
| commit | 891b91867a8b56616fc70b4ab982ad598bef2e54 (patch) | |
| tree | fb16dc4fda26151483e096e2da031ce869e4d62d | |
| parent | b2a815aa5454d3351d60b21123bf3c8f076094b0 (diff) | |
| download | mullvadvpn-891b91867a8b56616fc70b4ab982ad598bef2e54.tar.xz mullvadvpn-891b91867a8b56616fc70b4ab982ad598bef2e54.zip | |
Use bundled CA
| -rw-r--r-- | mullvad-daemon/src/bin/list-relays.rs | 4 | ||||
| -rw-r--r-- | mullvad-daemon/src/geoip.rs | 5 | ||||
| -rw-r--r-- | mullvad-daemon/src/main.rs | 19 | ||||
| -rw-r--r-- | mullvad-paths/src/lib.rs | 2 | ||||
| -rw-r--r-- | mullvad-paths/src/resources.rs | 6 | ||||
| -rw-r--r-- | mullvad-problem-report/src/main.rs | 4 | ||||
| -rw-r--r-- | mullvad-rpc/Cargo.toml | 5 | ||||
| -rw-r--r-- | mullvad-rpc/src/https_client_with_sni.rs | 74 | ||||
| -rw-r--r-- | mullvad-rpc/src/lib.rs | 20 | ||||
| -rw-r--r-- | mullvad-rpc/src/rest.rs | 13 |
10 files changed, 87 insertions, 65 deletions
diff --git a/mullvad-daemon/src/bin/list-relays.rs b/mullvad-daemon/src/bin/list-relays.rs index 581ef06d1a..a330297e20 100644 --- a/mullvad-daemon/src/bin/list-relays.rs +++ b/mullvad-daemon/src/bin/list-relays.rs @@ -9,6 +9,7 @@ #[macro_use] extern crate error_chain; +extern crate mullvad_paths; extern crate mullvad_rpc; extern crate serde_json; @@ -17,7 +18,8 @@ error_chain!{} quick_main!(run); fn run() -> Result<()> { - let mut rpc_manager = mullvad_rpc::MullvadRpcFactory::new(); + let ca_path = mullvad_paths::resources::get_api_ca_path(); + let mut rpc_manager = mullvad_rpc::MullvadRpcFactory::new(ca_path); let rpc_http_handle = rpc_manager .new_connection() .chain_err(|| "Unable to connect RPC")?; diff --git a/mullvad-daemon/src/geoip.rs b/mullvad-daemon/src/geoip.rs index 4118ed7871..01f42ec6c9 100644 --- a/mullvad-daemon/src/geoip.rs +++ b/mullvad-daemon/src/geoip.rs @@ -4,7 +4,7 @@ use mullvad_types::location::GeoIpLocation; use serde_json; -static URI: &str = "https://am.i.mullvad.net/json"; +const URI: &str = "https://am.i.mullvad.net/json"; error_chain! { errors { @@ -18,12 +18,13 @@ error_chain! { } } + pub fn send_location_request( request_sender: mullvad_rpc::rest::RequestSender, ) -> Box<Future<Item = GeoIpLocation, Error = Error>> { let (response_tx, response_rx) = futures::sync::oneshot::channel(); let request = mullvad_rpc::rest::create_get_request(URI.parse().unwrap()); - let future = futures::Sink::send(request_sender, (request, response_tx)) + let future = futures::Sink::send(request_sender.clone(), (request, response_tx)) .map_err(|e| Error::with_chain(e, ErrorKind::NoResponse)) .and_then(|_| response_rx.map_err(|e| Error::with_chain(e, ErrorKind::NoResponse))) .and_then(|response_result| response_result.map_err(Error::from)) diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs index d5e50f88b0..d2dc1da4f7 100644 --- a/mullvad-daemon/src/main.rs +++ b/mullvad-daemon/src/main.rs @@ -199,7 +199,7 @@ struct Daemon { settings: settings::Settings, accounts_proxy: AccountsProxy<HttpHandle>, version_proxy: AppVersionProxy<HttpHandle>, - http_handle: mullvad_rpc::rest::RequestSender, + https_handle: mullvad_rpc::rest::RequestSender, tokio_remote: tokio_core::reactor::Remote, relay_selector: relays::RelaySelector, firewall: FirewallProxy, @@ -220,19 +220,20 @@ impl Daemon { !rpc_uniqueness_check::is_another_instance_running(), ErrorKind::DaemonIsAlreadyRunning ); + let ca_path = resource_dir.join(mullvad_paths::resources::API_CA_FILENAME); - let mut rpc_manager = mullvad_rpc::MullvadRpcFactory::with_cache_dir(&cache_dir); + let mut rpc_manager = mullvad_rpc::MullvadRpcFactory::with_cache_dir(&cache_dir, &ca_path); - let (rpc_handle, http_handle, tokio_remote) = + let (rpc_handle, https_handle, tokio_remote) = mullvad_rpc::event_loop::create(move |core| { let handle = core.handle(); let rpc = rpc_manager.new_connection_on_event_loop(&handle); - let http = mullvad_rpc::rest::create_http_client(&handle); + let https_handle = mullvad_rpc::rest::create_https_client(&ca_path, &handle); let remote = core.remote(); - (rpc, http, remote) + (rpc, https_handle, remote) }).chain_err(|| "Unable to initialize network event loop")?; let rpc_handle = rpc_handle.chain_err(|| "Unable to create RPC client")?; - let http_handle = http_handle.chain_err(|| "Unable to create HTTP client")?; + let https_handle = https_handle.chain_err(|| "Unable to create am.i.mullvad client")?; let relay_selector = Self::create_relay_selector(rpc_handle.clone(), &resource_dir, &cache_dir); @@ -257,7 +258,7 @@ impl Daemon { settings: settings::Settings::load().chain_err(|| "Unable to read settings")?, accounts_proxy: AccountsProxy::new(rpc_handle.clone()), version_proxy: AppVersionProxy::new(rpc_handle), - http_handle, + https_handle, tokio_remote, relay_selector, firewall: FirewallProxy::new(&cache_dir).chain_err(|| ErrorKind::FirewallError)?, @@ -432,9 +433,9 @@ impl Daemon { }; Self::oneshot_send(tx, geo_ip_location, "current location"); } else { - let http_handle = self.http_handle.clone(); + let https_handle = self.https_handle.clone(); self.tokio_remote.spawn(move |_| { - geoip::send_location_request(http_handle) + geoip::send_location_request(https_handle) .map(move |location| Self::oneshot_send(tx, location, "current location")) .map_err(|e| { warn!("Unable to fetch GeoIP location: {}", e.display_chain()); diff --git a/mullvad-paths/src/lib.rs b/mullvad-paths/src/lib.rs index 12c209c718..e7a8ffbc9e 100644 --- a/mullvad-paths/src/lib.rs +++ b/mullvad-paths/src/lib.rs @@ -48,7 +48,7 @@ pub use cache::cache_dir; mod logs; pub use logs::{get_log_dir, log_dir}; -mod resources; +pub mod resources; pub use resources::get_resource_dir; mod rpc_address; diff --git a/mullvad-paths/src/resources.rs b/mullvad-paths/src/resources.rs index 4f60a88287..60c4406620 100644 --- a/mullvad-paths/src/resources.rs +++ b/mullvad-paths/src/resources.rs @@ -1,6 +1,8 @@ use std::env; use std::path::PathBuf; +pub const API_CA_FILENAME: &str = "api_root_ca.pem"; + pub fn get_resource_dir() -> PathBuf { match env::var_os("MULLVAD_RESOURCE_DIR") { Some(path) => PathBuf::from(path), @@ -23,3 +25,7 @@ fn get_default_resource_dir() -> PathBuf { } } } + +pub fn get_api_ca_path() -> PathBuf { + get_resource_dir().join(API_CA_FILENAME) +} diff --git a/mullvad-problem-report/src/main.rs b/mullvad-problem-report/src/main.rs index 1f63d1aa19..0749aa2946 100644 --- a/mullvad-problem-report/src/main.rs +++ b/mullvad-problem-report/src/main.rs @@ -219,7 +219,9 @@ fn send_problem_report(user_email: &str, user_message: &str, report_path: &Path) ); let metadata = collect_metadata(); - let mut rpc_manager = mullvad_rpc::MullvadRpcFactory::new(); + let ca_path = mullvad_paths::resources::get_api_ca_path(); + + let mut rpc_manager = mullvad_rpc::MullvadRpcFactory::new(ca_path); let rpc_http_handle = rpc_manager .new_connection() .chain_err(|| ErrorKind::RpcError)?; diff --git a/mullvad-rpc/Cargo.toml b/mullvad-rpc/Cargo.toml index 2f34fdd800..944de63245 100644 --- a/mullvad-rpc/Cargo.toml +++ b/mullvad-rpc/Cargo.toml @@ -15,10 +15,9 @@ lazy_static = "1.0" serde_json = "1.0" tokio-core = "0.1" hyper = "0.11" -hyper-tls = "0.1" -native-tls = "0.1" +hyper-openssl = "0.5" tokio-service = "0.1" -tokio-tls = "0.1" +tokio-openssl = "0.2" log = "0.4" mullvad-types = { path = "../mullvad-types" } diff --git a/mullvad-rpc/src/https_client_with_sni.rs b/mullvad-rpc/src/https_client_with_sni.rs index 319576da5e..9c91cc8e7f 100644 --- a/mullvad-rpc/src/https_client_with_sni.rs +++ b/mullvad-rpc/src/https_client_with_sni.rs @@ -1,43 +1,43 @@ +extern crate tokio_openssl; extern crate tokio_service; -extern crate tokio_tls; use std::fmt; use std::io; +use std::path::{Path, PathBuf}; use std::str; use std::sync::Arc; use futures::{Future, Poll}; use hyper::client::{Client, Connect, HttpConnector}; use hyper::{Body, Uri}; -use hyper_tls::MaybeHttpsStream; +pub use hyper_openssl::openssl::error::ErrorStack; +use hyper_openssl::openssl::ssl::{SslConnector, SslMethod}; use jsonrpc_client_http::ClientCreator; -pub use native_tls::Error; -use native_tls::TlsConnector; use tokio_core::reactor::Handle; +use self::tokio_openssl::{SslConnectorExt, SslStream}; use self::tokio_service::Service; -use self::tokio_tls::TlsConnectorExt; - -/// Number of threads in the thread pool doing DNS resolutions. -/// Since DNS is resolved via blocking syscall they must be run on separate threads. -static DNS_THREADS: usize = 2; pub struct HttpsClientWithSni { sni_hostname: String, + ca_path: Box<Path>, } impl HttpsClientWithSni { - pub fn new(sni_hostname: String) -> Self { - HttpsClientWithSni { sni_hostname } + pub fn new<P: Into<PathBuf>>(sni_hostname: String, ca_path: P) -> Self { + HttpsClientWithSni { + sni_hostname, + ca_path: ca_path.into().into_boxed_path(), + } } } impl ClientCreator for HttpsClientWithSni { type Connect = HttpsConnectorWithSni<HttpConnector>; - type Error = Error; + type Error = ErrorStack; fn create(&self, handle: &Handle) -> Result<Client<Self::Connect, Body>, Self::Error> { - let mut connector = HttpsConnectorWithSni::new(DNS_THREADS, handle)?; + let mut connector = HttpsConnectorWithSni::new(&self.ca_path, handle)?; connector.set_sni_hostname(Some(self.sni_hostname.clone())); let client = Client::configure().connector(connector).build(handle); Ok(client) @@ -49,7 +49,7 @@ impl ClientCreator for HttpsClientWithSni { pub struct HttpsConnectorWithSni<T> { sni_hostname: Option<String>, http: T, - tls: Arc<TlsConnector>, + tls: Arc<SslConnector>, } impl HttpsConnectorWithSni<HttpConnector> { @@ -59,11 +59,14 @@ impl HttpsConnectorWithSni<HttpConnector> { /// /// This uses hyper's default `HttpConnector`, and default `TlsConnector`. /// If you wish to use something besides the defaults, use `From::from`. - fn new(threads: usize, handle: &Handle) -> Result<Self, Error> { - let mut http = HttpConnector::new(threads, handle); + pub fn new<P: AsRef<Path>>(ca_path: P, handle: &Handle) -> Result<Self, ErrorStack> { + let mut http = HttpConnector::new(::DNS_THREADS, handle); http.enforce_http(false); - let tls = TlsConnector::builder()?.build()?; - Ok(HttpsConnectorWithSni::from((http, tls))) + let mut ssl_builder = SslConnector::builder(SslMethod::tls())?; + ssl_builder.set_ca_file(ca_path)?; + let ssl = ssl_builder.build(); + + Ok(HttpsConnectorWithSni::from((http, ssl))) } } @@ -75,13 +78,13 @@ where /// /// Configures the TLS connection handshake to request a certificate for a given domain, /// instead of the domain obtained from the URI. Use `None` to use the domain from the URI. - fn set_sni_hostname(&mut self, hostname: Option<String>) { + pub fn set_sni_hostname(&mut self, hostname: Option<String>) { self.sni_hostname = hostname; } } -impl<T> From<(T, TlsConnector)> for HttpsConnectorWithSni<T> { - fn from(args: (T, TlsConnector)) -> HttpsConnectorWithSni<T> { +impl<T> From<(T, SslConnector)> for HttpsConnectorWithSni<T> { + fn from(args: (T, SslConnector)) -> HttpsConnectorWithSni<T> { HttpsConnectorWithSni { sni_hostname: None, http: args.0, @@ -98,12 +101,17 @@ impl<T> fmt::Debug for HttpsConnectorWithSni<T> { impl<T: Connect> Service for HttpsConnectorWithSni<T> { type Request = Uri; - type Response = MaybeHttpsStream<T::Output>; + type Response = SslStream<T::Output>; type Error = io::Error; type Future = HttpsConnecting<T::Output>; fn call(&self, uri: Uri) -> Self::Future { - let is_https = uri.scheme() == Some("https"); + if uri.scheme() != Some("https") { + return HttpsConnecting(Box::new(::futures::future::err(io::Error::new( + io::ErrorKind::InvalidInput, + "invalid url, not https", + )))); + } let maybe_host = self .sni_hostname .as_ref() @@ -122,28 +130,22 @@ impl<T: Connect> Service for HttpsConnectorWithSni<T> { let connecting = self.http.connect(uri); let tls = self.tls.clone(); - let fut: BoxedFut<T::Output> = if is_https { - let fut = connecting.and_then(move |tcp| { - tls.connect_async(&host, tcp) - .map(|conn| MaybeHttpsStream::Https(conn)) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) - }); - Box::new(fut) - } else { - Box::new(connecting.map(|tcp| MaybeHttpsStream::Http(tcp))) - }; - HttpsConnecting(fut) + let fut = connecting.and_then(move |tcp| { + tls.connect_async(&host, tcp) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + }); + HttpsConnecting(Box::new(fut)) } } -type BoxedFut<T> = Box<Future<Item = MaybeHttpsStream<T>, Error = io::Error>>; +type BoxedFut<T> = Box<Future<Item = SslStream<T>, Error = io::Error>>; /// A Future representing work to connect to a URL, and a TLS handshake. pub struct HttpsConnecting<T>(BoxedFut<T>); impl<T> Future for HttpsConnecting<T> { - type Item = MaybeHttpsStream<T>; + type Item = SslStream<T>; type Error = io::Error; fn poll(&mut self) -> Poll<Self::Item, Self::Error> { diff --git a/mullvad-rpc/src/lib.rs b/mullvad-rpc/src/lib.rs index 02537adee3..6873fe926a 100644 --- a/mullvad-rpc/src/lib.rs +++ b/mullvad-rpc/src/lib.rs @@ -11,7 +11,7 @@ extern crate chrono; extern crate error_chain; extern crate futures; extern crate hyper; -extern crate hyper_tls; +extern crate hyper_openssl; #[macro_use] extern crate jsonrpc_client_core; extern crate jsonrpc_client_http; @@ -19,7 +19,6 @@ extern crate jsonrpc_client_http; extern crate lazy_static; #[macro_use] extern crate log; -extern crate native_tls; extern crate serde_json; extern crate tokio_core; @@ -40,7 +39,7 @@ use mullvad_types::version; use std::collections::HashMap; use std::net::{IpAddr, Ipv4Addr}; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::time::Duration; pub mod event_loop; @@ -50,7 +49,11 @@ mod cached_dns_resolver; use cached_dns_resolver::CachedDnsResolver; mod https_client_with_sni; -use https_client_with_sni::HttpsClientWithSni; +use https_client_with_sni::{HttpsClientWithSni, HttpsConnectorWithSni}; + +/// Number of threads in the thread pool doing DNS resolutions. +/// Since DNS is resolved via blocking syscall they must be run on separate threads. +const DNS_THREADS: usize = 2; const API_HOST: &str = "api.mullvad.net"; const RPC_TIMEOUT: Duration = Duration::from_secs(5); @@ -63,23 +66,26 @@ lazy_static! { /// A type that helps with the creation of RPC connections. pub struct MullvadRpcFactory { address_cache: Option<CachedDnsResolver>, + ca_path: PathBuf, } impl MullvadRpcFactory { /// Create a new `MullvadRpcFactory`. - pub fn new() -> Self { + pub fn new<P: Into<PathBuf>>(ca_path: P) -> Self { MullvadRpcFactory { address_cache: None, + ca_path: ca_path.into(), } } /// Create a new `MullvadRpcFactory` using the specified cache directory. - pub fn with_cache_dir(cache_dir: &Path) -> Self { + pub fn with_cache_dir<P: Into<PathBuf>>(cache_dir: &Path, ca_path: P) -> Self { let cache_file = cache_dir.join(API_IP_CACHE_FILENAME); let cached_dns_resolver = CachedDnsResolver::new(API_HOST.to_owned(), cache_file, *API_IP); MullvadRpcFactory { address_cache: Some(cached_dns_resolver), + ca_path: ca_path.into(), } } @@ -101,7 +107,7 @@ impl MullvadRpcFactory { F: FnOnce(HttpTransportBuilder<HttpsClientWithSni>) -> jsonrpc_client_http::Result<HttpTransport>, { - let client = HttpsClientWithSni::new(API_HOST.to_owned()); + let client = HttpsClientWithSni::new(API_HOST.to_owned(), self.ca_path.clone()); let transport_builder = HttpTransportBuilder::with_client(client).timeout(RPC_TIMEOUT); let transport = create_transport(transport_builder)?; diff --git a/mullvad-rpc/src/rest.rs b/mullvad-rpc/src/rest.rs index 0d8ed16027..988dfba7c0 100644 --- a/mullvad-rpc/src/rest.rs +++ b/mullvad-rpc/src/rest.rs @@ -1,14 +1,16 @@ +use std::path::Path; + use futures::sync::{mpsc, oneshot}; use futures::{future, Future, Stream}; use hyper; use hyper::client::Client; use hyper::{Request, StatusCode, Uri}; -use hyper_tls::HttpsConnector; -use native_tls; +use hyper_openssl::openssl::error::ErrorStack; use tokio_core::reactor::Handle; +use HttpsConnectorWithSni; error_chain! { errors { @@ -19,9 +21,9 @@ error_chain! { } } foreign_links { - Tls(native_tls::Error); Hyper(hyper::Error) #[doc = "An error occured in Hyper."]; Uri(hyper::error::UriError) #[doc = "The string given was not a valid URI."]; + OpenSsl(ErrorStack) #[doc = "Error in OpenSSL"]; } } @@ -29,9 +31,10 @@ error_chain! { pub type RequestSender = mpsc::UnboundedSender<(Request, oneshot::Sender<Result<Vec<u8>>>)>; type RequestReceiver = mpsc::UnboundedReceiver<(Request, oneshot::Sender<Result<Vec<u8>>>)>; -pub fn create_http_client(handle: &Handle) -> Result<RequestSender> { - let connector = HttpsConnector::new(1, handle)?; +pub fn create_https_client<P: AsRef<Path>>(ca_path: P, handle: &Handle) -> Result<RequestSender> { + let connector = HttpsConnectorWithSni::new(ca_path, handle)?; let client = Client::configure().connector(connector).build(handle); + let (request_tx, request_rx) = mpsc::unbounded(); handle.spawn(create_request_processing_future(request_rx, client)); Ok(request_tx) |
