summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorEmīls Piņķis <emils@mullvad.net>2019-02-27 15:33:46 +0000
committerEmīls Piņķis <emils@mullvad.net>2019-02-27 15:33:46 +0000
commit502bf6fa28820fcfe9f1a16bf208d09191792cd7 (patch)
tree3060c10fb531e5687e57126318b83aaf50248180
parent068b0bdea9bad08c9ade30fa70d7619db89344d8 (diff)
parenta05b2329ce32aa929eed1903d8fdd63780a8127d (diff)
downloadmullvadvpn-502bf6fa28820fcfe9f1a16bf208d09191792cd7.tar.xz
mullvadvpn-502bf6fa28820fcfe9f1a16bf208d09191792cd7.zip
Merge branch 'wg-key-management'
-rw-r--r--Cargo.lock76
-rw-r--r--mullvad-cli/src/cmds/tunnel.rs39
-rw-r--r--mullvad-daemon/src/account_history.rs188
-rw-r--r--mullvad-daemon/src/lib.rs210
-rw-r--r--mullvad-daemon/src/management_interface.rs123
-rw-r--r--mullvad-ipc-client/src/lib.rs17
-rw-r--r--mullvad-rpc/Cargo.toml1
-rw-r--r--mullvad-rpc/src/lib.rs14
-rw-r--r--mullvad-types/Cargo.toml1
-rw-r--r--mullvad-types/src/lib.rs1
-rw-r--r--mullvad-types/src/wireguard.rs17
-rw-r--r--talpid-types/Cargo.toml2
-rw-r--r--talpid-types/src/net/wireguard.rs26
13 files changed, 577 insertions, 138 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 95fb30478c..d564c72f74 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -166,6 +166,14 @@ dependencies = [
]
[[package]]
+name = "clear_on_drop"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -265,6 +273,18 @@ dependencies = [
]
[[package]]
+name = "curve25519-dalek"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "dbus"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -302,6 +322,14 @@ dependencies = [
]
[[package]]
+name = "digest"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "dirs"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -513,6 +541,14 @@ version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "generic-array"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "globset"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -642,6 +678,14 @@ dependencies = [
[[package]]
name = "ipnetwork"
version = "0.14.0"
+source = "git+https://github.com/mullvad/ipnetwork?branch=fix-deserialization#10d12cd94509f98cbc0d60d9f42380615faee638"
+dependencies = [
+ "serde 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ipnetwork"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1156,6 +1200,7 @@ dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mullvad-types 0.1.0",
"serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
+ "talpid-types 0.1.0",
"tempfile 3.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-openssl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1189,6 +1234,7 @@ version = "0.1.0"
dependencies = [
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ipnetwork 0.14.0 (git+https://github.com/mullvad/ipnetwork?branch=fix-deserialization)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mullvad-paths 0.1.0",
@@ -1884,6 +1930,11 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "subtle"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2036,7 +2087,9 @@ dependencies = [
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ipnetwork 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.87 (registry+https://github.com/rust-lang/crates.io-index)",
+ "x25519-dalek 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2362,6 +2415,11 @@ dependencies = [
]
[[package]]
+name = "typenum"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "ucd-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2552,6 +2610,16 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "x25519-dalek"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "curve25519-dalek 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[metadata]
"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
@@ -2574,6 +2642,7 @@ dependencies = [
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
+"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum colored 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e9a455e156a4271e12fd0246238c380b1e223e3736663c7a18ed8b6362028a9"
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
@@ -2585,10 +2654,12 @@ dependencies = [
"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4"
"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c"
"checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e"
+"checksum curve25519-dalek 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e1f8a6fc0376eb52dc18af94915cc04dfdf8353746c0e8c550ae683a0815e5c1"
"checksum dbus 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e1b39f3f6aa3d4a1522c4f0f9f1e9e9167bd93740a8690874caa7cf8ce47d7"
"checksum derive-try-from-primitive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81dbd65eb15734b6d50dc6ac86f14f928462be0a5df6bda17761e909071ede5d"
"checksum derive_builder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c998e6ab02a828dd9735c18f154e14100e674ed08cb4e1938f0e4177543f439"
"checksum derive_builder_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "735e24ee9e5fa8e16b86da5007856e97d592e11867e45d76e0c0d0a164a0b757"
+"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
"checksum dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88972de891f6118092b643d85a0b28e0678e0f948d7f879aa32f2d5aafe97d2a"
"checksum duct 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f87f5af80601599209bff21146e4113e8c54471151049deebc37e75b78e42729"
"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38"
@@ -2614,6 +2685,7 @@ dependencies = [
"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b"
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
+"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592"
"checksum globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90d069fe6beb9be359ef505650b3f73228c5591a3c4b1f32be2f4f44459ffa3a"
"checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865"
"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
@@ -2626,6 +2698,7 @@ dependencies = [
"checksum ioctl-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2c4b26352496eaaa8ca7cfa9bd99e93419d3f7983dc6e99c2a35fe9e33504a"
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
"checksum ipnetwork 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d1d8b990621b5b0806fac3dbf71d1833a4c0a9e25702d10bd8b2c629c7ae01c"
+"checksum ipnetwork 0.14.0 (git+https://github.com/mullvad/ipnetwork?branch=fix-deserialization)" = "<none>"
"checksum ipnetwork 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3d862c86f7867f19b693ec86765e0252d82e53d4240b9b629815675a0714ad1"
"checksum iproute2 0.0.2 (git+https://github.com/mullvad/netlink?branch=ignore-hw-address)" = "<none>"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
@@ -2745,6 +2818,7 @@ dependencies = [
"checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
+"checksum subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "702662512f3ddeb74a64ce2fbbf3707ee1b6bb663d28bb054e0779bbc720d926"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
@@ -2780,6 +2854,7 @@ dependencies = [
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
"checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2"
"checksum tun 0.4.2 (git+https://github.com/pinkisemils/rust-tun?branch=add-raw-fd-traits)" = "<none>"
+"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
"checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
@@ -2808,3 +2883,4 @@ dependencies = [
"checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a"
"checksum winres 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "aea48d0b4b15ba195fc7250fdfb27736ce3bc327535db1c87a2fcb62371f9ecd"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
+"checksum x25519-dalek 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "58e75de7a035f694df91838afa87faa8278bc484fa8d7dc34b5a24535cc2bb41"
diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs
index bbd1a0e2a1..0c05c1946d 100644
--- a/mullvad-cli/src/cmds/tunnel.rs
+++ b/mullvad-cli/src/cmds/tunnel.rs
@@ -38,7 +38,8 @@ fn create_wireguard_subcommand() -> clap::App<'static, 'static> {
let app = clap::SubCommand::with_name("wireguard")
.about("Manage options for Wireguard tunnels")
.setting(clap::AppSettings::SubcommandRequired)
- .subcommand(create_wireguard_mtu_subcommand());
+ .subcommand(create_wireguard_mtu_subcommand())
+ .subcommand(create_wireguard_keys_subcommand());
if cfg!(target_os = "linux") {
app.subcommand(create_wireguard_fwmark_subcommand())
} else {
@@ -57,6 +58,14 @@ fn create_wireguard_mtu_subcommand() -> clap::App<'static, 'static> {
)
}
+fn create_wireguard_keys_subcommand() -> clap::App<'static, 'static> {
+ clap::SubCommand::with_name("key")
+ .about("Manage your wireguard keys")
+ .setting(clap::AppSettings::SubcommandRequired)
+ .subcommand(clap::SubCommand::with_name("check"))
+ .subcommand(clap::SubCommand::with_name("generate"))
+}
+
fn create_wireguard_fwmark_subcommand() -> clap::App<'static, 'static> {
clap::SubCommand::with_name("fwmark")
.about("Configure the firewall mark used to direct traffic through Wireguard tunnel")
@@ -227,6 +236,12 @@ impl Tunnel {
_ => unreachable!("unhandled command"),
},
+ ("key", Some(matches)) => match matches.subcommand() {
+ ("check", _) => Self::process_wireguard_key_check(),
+ ("generate", _) => Self::process_wireguard_key_generate(),
+ _ => unreachable!("unhandled command"),
+ },
+
#[cfg(target_os = "linux")]
("fwmark", Some(matches)) => match matches.subcommand() {
("get", _) => Self::process_wireguard_fwmark_get(),
@@ -265,6 +280,28 @@ impl Tunnel {
Ok(())
}
+ fn process_wireguard_key_check() -> Result<()> {
+ let mut rpc = new_rpc_client()?;
+ match rpc.get_wireguard_key()? {
+ Some(key) => {
+ println!("Current key: {}", key);
+ }
+ None => {
+ println!("No key is set");
+ return Ok(());
+ }
+ };
+
+ let is_valid = rpc.verify_wireguard_key()?;
+ println!("Key is valid for use with current account: {}", is_valid);
+ Ok(())
+ }
+
+ fn process_wireguard_key_generate() -> Result<()> {
+ let mut rpc = new_rpc_client()?;
+ rpc.generate_wireguard_key().map_err(|e| e.into())
+ }
+
#[cfg(target_os = "linux")]
fn process_wireguard_fwmark_get() -> Result<()> {
let tunnel_options = Self::get_tunnel_options()?;
diff --git a/mullvad-daemon/src/account_history.rs b/mullvad-daemon/src/account_history.rs
index de57b60d3f..e79356d257 100644
--- a/mullvad-daemon/src/account_history.rs
+++ b/mullvad-daemon/src/account_history.rs
@@ -1,22 +1,18 @@
-use mullvad_types::account::AccountToken;
+use mullvad_types::{account::AccountToken, wireguard::WireguardData};
use std::{
- fs::File,
- io,
- path::{Path, PathBuf},
+ collections::VecDeque,
+ fs,
+ io::{self, Seek, Write},
+ path::Path,
};
error_chain! {
errors {
- ReadError(path: PathBuf) {
+ ReadError {
description("Unable to read account history file")
- display("Unable to read account history from {}", path.display())
}
- WriteError(path: PathBuf) {
+ WriteError {
description("Unable to write account history file")
- display("Unable to write account history to {}", path.display())
- }
- ParseError {
- description("Malformed account history")
}
}
}
@@ -24,84 +20,142 @@ error_chain! {
static ACCOUNT_HISTORY_FILE: &str = "account-history.json";
static ACCOUNT_HISTORY_LIMIT: usize = 3;
-#[derive(Debug, Clone, Deserialize, Serialize, Default)]
+/// A trivial MRU cache of account data
pub struct AccountHistory {
- accounts: Vec<AccountToken>,
- #[serde(skip)]
- cache_path: PathBuf,
+ file: io::BufWriter<fs::File>,
+ accounts: VecDeque<AccountEntry>,
}
+
impl AccountHistory {
- /// Returns a new empty `AccountHistory` ready to load from, or save to, the given cache dir.
- pub fn new(cache_dir: &Path) -> AccountHistory {
- AccountHistory {
- accounts: Vec::new(),
- cache_path: cache_dir.join(ACCOUNT_HISTORY_FILE),
+ pub fn new(cache_dir: &Path) -> Result<AccountHistory> {
+ let mut options = fs::OpenOptions::new();
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::OpenOptionsExt;
+ options.mode(0o600);
}
- }
+ #[cfg(windows)]
+ {
+ use std::os::windows::fs::OpenOptionsExt;
+ // a share mode of zero ensures exclusive access to the file to *this* process
+ options.share_mode(0);
+ }
+ let path = cache_dir.join(ACCOUNT_HISTORY_FILE);
+ log::info!("Opening account history file in {}", path.display());
+ let mut reader = options
+ .write(true)
+ .read(true)
+ .create(true)
+ .open(path)
+ .map(io::BufReader::new)
+ .chain_err(|| ErrorKind::ReadError)?;
- /// Loads account history from file. If no file is present this does nothing.
- pub fn load(&mut self) -> Result<()> {
- match File::open(&self.cache_path).map(io::BufReader::new) {
- Ok(mut file) => {
- log::info!(
- "Loading account history from {}",
- &self.cache_path.display()
- );
- self.accounts = Self::parse(&mut file)?.accounts;
- Ok(())
+ let accounts: VecDeque<AccountEntry> = match serde_json::from_reader(&mut reader) {
+ Err(e) => {
+ log::error!("Failed to read account history - {}", e);
+ Self::try_old_format(&mut reader)?
+ .into_iter()
+ .map(|account| AccountEntry {
+ account,
+ wireguard: None,
+ })
+ .collect()
}
- Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
- log::info!("No account history file at {}", &self.cache_path.display());
- Ok(())
- }
- Err(e) => Err(e).chain_err(|| ErrorKind::ReadError(self.cache_path.clone())),
- }
+ Ok(accounts) => accounts,
+ };
+ let file = io::BufWriter::new(reader.into_inner());
+ Ok(AccountHistory { file, accounts })
+ }
+
+ fn try_old_format(reader: &mut io::BufReader<fs::File>) -> Result<Vec<AccountToken>> {
+ reader
+ .seek(io::SeekFrom::Start(0))
+ .chain_err(|| ErrorKind::ReadError)?;
+ Ok(serde_json::from_reader(reader).unwrap_or(vec![]))
}
- fn parse(file: &mut impl io::Read) -> Result<AccountHistory> {
- serde_json::from_reader(file).chain_err(|| ErrorKind::ParseError)
+ /// Gets account data for a certain account id and bumps it's entry to the top of the list if
+ /// it isn't there already. Returns None if the account entry is not available.
+ pub fn get(&mut self, account: &AccountToken) -> Result<Option<AccountEntry>> {
+ let (idx, entry) = match self
+ .accounts
+ .iter()
+ .enumerate()
+ .find(|(_idx, entry)| &entry.account == account)
+ {
+ Some((idx, entry)) => (idx, entry.clone()),
+ None => {
+ return Ok(None);
+ }
+ };
+ // this account is already on top
+ if idx == 0 {
+ return Ok(Some(entry));
+ }
+ self.insert(entry.clone())?;
+ Ok(Some(entry))
}
- pub fn get_accounts(&self) -> &[AccountToken] {
- &self.accounts
+ /// Bumps history of an account token. If the account token is not in history, it will be
+ /// added.
+ pub fn bump_history(&mut self, account: &AccountToken) -> Result<()> {
+ if let None = self.get(account)? {
+ let new_entry = AccountEntry {
+ account: account.to_string(),
+ wireguard: None,
+ };
+ self.insert(new_entry)?;
+ }
+ Ok(())
}
- /// Add account token to the account history removing duplicate entries
- pub fn add_account_token(&mut self, account_token: AccountToken) -> Result<()> {
+ /// Always inserts a new entry at the start of the list
+ pub fn insert(&mut self, new_entry: AccountEntry) -> Result<()> {
self.accounts
- .retain(|existing_token| existing_token != &account_token);
- self.accounts.push(account_token);
+ .retain(|entry| entry.account != new_entry.account);
- let num_accounts = self.accounts.len();
- if num_accounts > ACCOUNT_HISTORY_LIMIT {
- self.accounts = self
- .accounts
- .split_off(num_accounts - ACCOUNT_HISTORY_LIMIT);
+ self.accounts.push_front(new_entry);
+ if self.accounts.len() > ACCOUNT_HISTORY_LIMIT {
+ let _ = self.accounts.pop_back();
}
-
- self.save()
+ self.save_to_disk()
}
- /// Remove account token from the account history
- pub fn remove_account_token(&mut self, account_token: &AccountToken) -> Result<()> {
+ /// Retrieve account history.
+ pub fn get_account_history(&self) -> Vec<AccountToken> {
self.accounts
- .retain(|existing_token| existing_token != account_token);
- self.save()
+ .iter()
+ .map(|entry| entry.account.clone())
+ .collect()
}
- /// Serializes the account history and saves it to the file it was loaded from.
- fn save(&self) -> Result<()> {
- log::debug!("Writing account history to {}", self.cache_path.display());
- let mut file = File::create(&self.cache_path)
- .map(io::BufWriter::new)
- .chain_err(|| ErrorKind::WriteError(self.cache_path.clone()))?;
-
- serde_json::to_writer_pretty(&mut file, self)
- .chain_err(|| ErrorKind::WriteError(self.cache_path.clone()))?;
+ /// Remove account data
+ pub fn remove_account(&mut self, account: &str) -> Result<()> {
+ self.accounts.retain(|entry| entry.account != account);
+ self.save_to_disk()
+ }
- file.get_mut()
+ fn save_to_disk(&mut self) -> Result<()> {
+ self.file
+ .get_mut()
+ .set_len(0)
+ .chain_err(|| ErrorKind::WriteError)?;
+ self.file
+ .seek(io::SeekFrom::Start(0))
+ .chain_err(|| ErrorKind::WriteError)?;
+ serde_json::to_writer_pretty(&mut self.file, &self.accounts)
+ .chain_err(|| ErrorKind::WriteError)?;
+ self.file.flush().chain_err(|| ErrorKind::WriteError)?;
+ self.file
+ .get_mut()
.sync_all()
- .chain_err(|| ErrorKind::WriteError(self.cache_path.clone()))
+ .chain_err(|| ErrorKind::WriteError)
}
}
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct AccountEntry {
+ pub account: AccountToken,
+ pub wireguard: Option<WireguardData>,
+}
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 0faf2b81ff..07d7cd3e4f 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -26,7 +26,7 @@ use futures::{
Future, Sink,
};
use log::{debug, error, info, warn};
-use mullvad_rpc::{AccountsProxy, AppVersionProxy, HttpHandle};
+use mullvad_rpc::{AccountsProxy, AppVersionProxy, HttpHandle, WireguardKeyProxy};
use mullvad_types::{
account::{AccountData, AccountToken},
endpoint::MullvadEndpoint,
@@ -46,7 +46,7 @@ use talpid_core::{
tunnel_state_machine::{self, TunnelCommand, TunnelParametersGenerator},
};
use talpid_types::{
- net::{openvpn, TransportProtocol, TunnelParameters},
+ net::{openvpn, wireguard, TransportProtocol, TunnelParameters},
tunnel::{BlockReason, TunnelStateTransition},
};
@@ -66,9 +66,13 @@ error_chain! {
description("Error in the management interface")
display("Management interface error: {}", msg)
}
+ NoKeyAvailable {
+ description("No wireguard private key available")
+ }
}
links {
TunnelError(tunnel_state_machine::Error, tunnel_state_machine::ErrorKind);
+ AccountHistory(account_history::Error, account_history::ErrorKind);
}
}
@@ -156,6 +160,8 @@ pub struct Daemon {
#[cfg(unix)]
management_interface_socket_path: String,
settings: Settings,
+ account_history: account_history::AccountHistory,
+ wg_key_proxy: WireguardKeyProxy<HttpHandle>,
accounts_proxy: AccountsProxy<HttpHandle>,
version_proxy: AppVersionProxy<HttpHandle>,
https_handle: mullvad_rpc::rest::RequestSender,
@@ -195,6 +201,9 @@ impl Daemon {
let relay_selector =
relays::RelaySelector::new(rpc_handle.clone(), &resource_dir, &cache_dir);
let settings = Settings::load().chain_err(|| "Unable to read settings")?;
+ let account_history = account_history::AccountHistory::new(&cache_dir)
+ .chain_err(|| "Unable to read wireguard key cache")?;
+
let (tx, rx) = mpsc::channel();
let tunnel_parameters_generator = MullvadTunnelParametersGenerator { tx: tx.clone() };
@@ -209,7 +218,7 @@ impl Daemon {
)?;
let target_state = TargetState::Unsecured;
- let management_interface_result = Self::start_management_interface(tx.clone(), cache_dir)?;
+ let management_interface_result = Self::start_management_interface(tx.clone())?;
// Attempt to download a fresh relay list
relay_selector.update();
@@ -226,6 +235,8 @@ impl Daemon {
#[cfg(unix)]
management_interface_socket_path: management_interface_result.1,
settings,
+ account_history,
+ wg_key_proxy: WireguardKeyProxy::new(rpc_handle.clone()),
accounts_proxy: AccountsProxy::new(rpc_handle.clone()),
version_proxy: AppVersionProxy::new(rpc_handle),
https_handle,
@@ -240,10 +251,9 @@ impl Daemon {
// Returns a handle that allows notifying all subscribers on events.
fn start_management_interface(
event_tx: mpsc::Sender<DaemonEvent>,
- cache_dir: PathBuf,
) -> Result<(management_interface::EventBroadcaster, String)> {
let multiplex_event_tx = IntoSender::from(event_tx.clone());
- let server = Self::start_management_interface_server(multiplex_event_tx, cache_dir)?;
+ let server = Self::start_management_interface_server(multiplex_event_tx)?;
let event_broadcaster = server.event_broadcaster();
let socket_path = server.socket_path().to_owned();
Self::spawn_management_interface_wait_thread(server, event_tx);
@@ -252,9 +262,8 @@ impl Daemon {
fn start_management_interface_server(
event_tx: IntoSender<ManagementCommand, DaemonEvent>,
- cache_dir: PathBuf,
) -> Result<ManagementInterfaceServer> {
- let server = ManagementInterfaceServer::start(event_tx, cache_dir)
+ let server = ManagementInterfaceServer::start(event_tx)
.chain_err(|| ErrorKind::ManagementInterfaceError("Failed to start server"))?;
info!(
"Mullvad management interface listening on {}",
@@ -370,7 +379,7 @@ impl Daemon {
}
fn create_tunnel_parameters(
- &self,
+ &mut self,
endpoint: MullvadEndpoint,
account_token: String,
) -> Result<TunnelParameters> {
@@ -382,10 +391,30 @@ impl Daemon {
generic_options: tunnel_options.generic,
}
.into()),
- MullvadEndpoint::Wireguard {
- peer: _,
- gateway: _,
- } => Err(ErrorKind::UnsupportedTunnel.into()),
+ MullvadEndpoint::Wireguard { peer, gateway } => {
+ let wg_data = self
+ .account_history
+ .get(&account_token)?
+ .and_then(|entry| entry.wireguard)
+ .ok_or(ErrorKind::NoKeyAvailable)?;
+ let tunnel = wireguard::TunnelConfig {
+ private_key: wg_data.private_key,
+ addresses: vec![
+ wg_data.addresses.ipv4_address.ip().into(),
+ wg_data.addresses.ipv6_address.ip().into(),
+ ],
+ };
+ Ok(wireguard::TunnelParameters {
+ connection: wireguard::ConnectionConfig {
+ tunnel,
+ peer,
+ gateway,
+ },
+ options: tunnel_options.wireguard,
+ generic_options: tunnel_options.generic,
+ }
+ .into())
+ }
}
}
@@ -423,6 +452,10 @@ impl Daemon {
GetRelayLocations(tx) => self.on_get_relay_locations(tx),
UpdateRelayLocations => self.on_update_relay_locations(),
SetAccount(tx, account_token) => self.on_set_account(tx, account_token),
+ GetAccountHistory(tx) => self.on_get_account_history(tx),
+ RemoveAccountFromHistory(tx, account_token) => {
+ self.on_remove_account_from_history(tx, account_token)
+ }
UpdateRelaySettings(tx, update) => self.on_update_relay_settings(tx, update),
SetAllowLan(tx, allow_lan) => self.on_set_allow_lan(tx, allow_lan),
SetBlockWhenDisconnected(tx, block_when_disconnected) => {
@@ -436,6 +469,9 @@ impl Daemon {
SetWireguardFwmark(tx, fwmark) => self.on_set_wireguard_fwmark(tx, fwmark),
SetWireguardMtu(tx, mtu) => self.on_set_wireguard_mtu(tx, mtu),
GetSettings(tx) => self.on_get_settings(tx),
+ GenerateWireguardKey(tx) => self.on_generate_wireguard_key(tx),
+ GetWireguardKey(tx) => self.on_get_wireguard_key(tx),
+ VerifyWireguardKey(tx) => self.on_verify_wireguard_key(tx),
GetVersionInfo(tx) => self.on_get_version_info(tx),
GetCurrentVersion(tx) => self.on_get_current_version(tx),
Shutdown => self.handle_trigger_shutdown_event(),
@@ -537,8 +573,7 @@ impl Daemon {
}
fn on_set_account(&mut self, tx: oneshot::Sender<()>, account_token: Option<String>) {
- let account_token_cleared = account_token.is_none();
- let save_result = self.settings.set_account_token(account_token);
+ let save_result = self.settings.set_account_token(account_token.clone());
match save_result.chain_err(|| "Unable to save settings") {
Ok(account_changed) => {
@@ -546,12 +581,18 @@ impl Daemon {
if account_changed {
self.management_interface_broadcaster
.notify_settings(&self.settings);
- if account_token_cleared {
- info!("Disconnecting because account token was cleared");
- self.set_target_state(TargetState::Unsecured);
- } else {
- info!("Initiating tunnel restart because the account token changed");
- self.reconnect_tunnel();
+ match account_token {
+ Some(token) => {
+ if let Err(e) = self.account_history.bump_history(&token) {
+ log::error!("Failed to bump account history: {}", e);
+ }
+ info!("Initiating tunnel restart because the account token changed");
+ self.reconnect_tunnel();
+ }
+ None => {
+ info!("Disconnecting because account token was cleared");
+ self.set_target_state(TargetState::Unsecured);
+ }
}
}
}
@@ -559,6 +600,24 @@ impl Daemon {
}
}
+ fn on_get_account_history(&mut self, tx: oneshot::Sender<Vec<AccountToken>>) {
+ Self::oneshot_send(
+ tx,
+ self.account_history.get_account_history(),
+ "get_account_history response",
+ );
+ }
+
+ fn on_remove_account_from_history(
+ &mut self,
+ tx: oneshot::Sender<()>,
+ account_token: AccountToken,
+ ) {
+ if let Ok(_) = self.account_history.remove_account(&account_token) {
+ Self::oneshot_send(tx, (), "remove_account_from_history response");
+ }
+ }
+
fn on_get_version_info(
&mut self,
tx: oneshot::Sender<BoxFuture<AppVersionInfo, mullvad_rpc::Error>>,
@@ -762,6 +821,117 @@ impl Daemon {
}
}
+ fn on_generate_wireguard_key(
+ &mut self,
+ tx: oneshot::Sender<::std::result::Result<(), mullvad_rpc::Error>>,
+ ) {
+ let mut result = || -> ::std::result::Result<(), String> {
+ let account_token = self
+ .settings
+ .get_account_token()
+ .ok_or("No account token set".to_string())?;
+
+ let mut account_entry = self
+ .account_history
+ .get(&account_token)
+ .map_err(|e| format!("Failed to read account entry from history: {}", e))
+ .map(|data| {
+ data.unwrap_or_else(|| {
+ log::error!("Account token set in settings but not in account history");
+ account_history::AccountEntry {
+ account: account_token.clone(),
+ wireguard: None,
+ }
+ })
+ })?;
+
+ let private_key = wireguard::PrivateKey::new_from_random()
+ .map_err(|e| format!("Failed to generate new key - {}", e))?;
+
+ let fut = self
+ .wg_key_proxy
+ .push_wg_key(account_token, private_key.public_key());
+
+ let mut core = tokio_core::reactor::Core::new()
+ .map_err(|e| format!("Failed to spawn future for pushing wg key - {}", e))?;
+
+ let addresses = core
+ .run(fut)
+ .map_err(|e| format!("Failed to push new wireguard key: {}", e))?;
+
+ account_entry.wireguard = Some(mullvad_types::wireguard::WireguardData {
+ private_key,
+ addresses,
+ });
+
+ self.account_history
+ .insert(account_entry)
+ .map_err(|e| format!("Failed to add new wireguard key to account data: {}", e))
+ };
+ match result() {
+ Ok(()) => {
+ Self::oneshot_send(tx, Ok(()), "generate_wireguard_key response");
+ }
+ Err(e) => {
+ log::error!("Failed to generate new wireguard key - {}", e);
+ }
+ }
+ }
+
+ fn on_get_wireguard_key(&mut self, tx: oneshot::Sender<Option<wireguard::PublicKey>>) {
+ let key = self
+ .settings
+ .get_account_token()
+ .and_then(|account| self.account_history.get(&account).ok()?)
+ .and_then(|account_entry| {
+ account_entry
+ .wireguard
+ .map(|wg| wg.private_key.public_key())
+ });
+
+ Self::oneshot_send(tx, key, "get_wireguard_key response");
+ }
+
+ fn on_verify_wireguard_key(&mut self, tx: oneshot::Sender<bool>) {
+ use futures::future::Executor;
+ let account = match self.settings.get_account_token() {
+ Some(account) => account,
+ None => {
+ Self::oneshot_send(tx, false, "verify_wireguard_key response");
+ return;
+ }
+ };
+
+ let key = self
+ .account_history
+ .get(&account)
+ .map(|entry| entry.and_then(|e| e.wireguard.map(|wg| wg.private_key.public_key())));
+
+ let public_key = match key {
+ Ok(Some(public_key)) => public_key,
+ Ok(None) => {
+ Self::oneshot_send(tx, false, "verify_wireguard_key response");
+ return;
+ }
+ Err(e) => {
+ log::error!("Failed to read key data: {}", e);
+ return;
+ }
+ };
+
+ let fut = self
+ .wg_key_proxy
+ .check_wg_key(account, public_key.clone())
+ .map(|is_valid| {
+ Self::oneshot_send(tx, is_valid, "verify_wireguard_key response");
+ ()
+ })
+ .map_err(|e| log::error!("Failed to verify wireguard key - {}", e));
+ if let Err(e) = self.tokio_remote.execute(fut) {
+ log::error!("Failed to spawn a future to verify wireguard key: {:?}", e);
+ }
+ }
+
fn on_get_settings(&self, tx: oneshot::Sender<Settings>) {
Self::oneshot_send(tx, self.settings.clone(), "get_settings response");
}
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index aeeda483a7..8b33e20171 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -1,4 +1,3 @@
-use crate::account_history::{AccountHistory, Error as AccountHistoryError};
use error_chain::ChainedError;
use jsonrpc_core::{
futures::{
@@ -25,12 +24,14 @@ use mullvad_types::{
use serde;
use std::{
collections::{hash_map::Entry, HashMap},
- path::PathBuf,
sync::{Arc, Mutex, RwLock},
};
use talpid_core::mpsc::IntoSender;
use talpid_ipc;
-use talpid_types::{net::openvpn, tunnel::TunnelStateTransition};
+use talpid_types::{
+ net::{openvpn, wireguard},
+ tunnel::TunnelStateTransition,
+};
use uuid;
/// FIXME(linus): This is here just because the futures crate has deprecated it and jsonrpc_core
@@ -133,6 +134,18 @@ build_rpc_trait! {
#[rpc(meta, name = "get_settings")]
fn get_settings(&self, Self::Metadata) -> BoxFuture<Settings, Error>;
+ /// Generates new wireguard key for current account
+ #[rpc(meta, name = "generate_wireguard_key")]
+ fn generate_wireguard_key(&self, Self::Metadata) -> BoxFuture<(), Error>;
+
+ /// Retrieve a public key for current account if the account has one.
+ #[rpc(meta, name = "get_wireguard_key")]
+ fn get_wireguard_key(&self, Self::Metadata) -> BoxFuture<Option<wireguard::PublicKey>, Error>;
+
+ /// Verify if current wireguard key is still valid
+ #[rpc(meta, name = "verify_wireguard_key")]
+ fn verify_wireguard_key(&self, Self::Metadata) -> BoxFuture<bool, Error>;
+
/// Retreive version of the app
#[rpc(meta, name = "get_current_version")]
fn get_current_version(&self, Self::Metadata) -> BoxFuture<String, Error>;
@@ -182,6 +195,10 @@ pub enum ManagementCommand {
OneshotSender<BoxFuture<AccountData, mullvad_rpc::Error>>,
AccountToken,
),
+ /// Request account history
+ GetAccountHistory(OneshotSender<Vec<AccountToken>>),
+ /// Request account history
+ RemoveAccountFromHistory(OneshotSender<()>, AccountToken),
/// Get the list of countries and cities where there are relays.
GetRelayLocations(OneshotSender<RelayList>),
/// Trigger an asynchronous relay list update. This returns before the relay list is actually
@@ -213,6 +230,12 @@ pub enum ManagementCommand {
SetWireguardMtu(OneshotSender<()>, Option<u16>),
/// Get the daemon settings
GetSettings(OneshotSender<Settings>),
+ /// Generate new wireguard key
+ GenerateWireguardKey(OneshotSender<Result<(), mullvad_rpc::Error>>),
+ /// Return a public key of the currently set wireguard private key, if there is one
+ GetWireguardKey(OneshotSender<Option<wireguard::PublicKey>>),
+ /// Verify if the currently set wireguard key is valid.
+ VerifyWireguardKey(OneshotSender<bool>),
/// Get information about the currently running and latest app versions
GetVersionInfo(OneshotSender<BoxFuture<version::AppVersionInfo, mullvad_rpc::Error>>),
/// Get current version of the app
@@ -233,14 +256,11 @@ pub struct ManagementInterfaceServer {
}
impl ManagementInterfaceServer {
- pub fn start<T>(
- tunnel_tx: IntoSender<ManagementCommand, T>,
- cache_dir: PathBuf,
- ) -> talpid_ipc::Result<Self>
+ pub fn start<T>(tunnel_tx: IntoSender<ManagementCommand, T>) -> talpid_ipc::Result<Self>
where
T: From<ManagementCommand> + 'static + Send,
{
- let rpc = ManagementInterface::new(tunnel_tx, cache_dir);
+ let rpc = ManagementInterface::new(tunnel_tx);
let subscriptions = rpc.subscriptions.clone();
let mut io = PubSubHandler::default();
@@ -309,15 +329,13 @@ impl EventBroadcaster {
struct ManagementInterface<T: From<ManagementCommand> + 'static + Send> {
subscriptions: Arc<ActiveSubscriptions>,
tx: Mutex<IntoSender<ManagementCommand, T>>,
- cache_dir: PathBuf,
}
impl<T: From<ManagementCommand> + 'static + Send> ManagementInterface<T> {
- pub fn new(tx: IntoSender<ManagementCommand, T>, cache_dir: PathBuf) -> Self {
+ pub fn new(tx: IntoSender<ManagementCommand, T>) -> Self {
ManagementInterface {
subscriptions: Default::default(),
tx: Mutex::new(tx),
- cache_dir,
}
}
@@ -381,12 +399,6 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterface<T> {
_ => Error::internal_error(),
}
}
-
- fn load_history(&self) -> Result<AccountHistory, AccountHistoryError> {
- let mut account_history = AccountHistory::new(&self.cache_dir);
- account_history.load()?;
- Ok(account_history)
- }
}
impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
@@ -440,18 +452,6 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
let future = self
.send_command_to_daemon(ManagementCommand::SetAccount(tx, account_token.clone()))
.and_then(|_| rx.map_err(|_| Error::internal_error()));
-
- if let Some(new_account_token) = account_token {
- if let Err(e) = self.load_history().and_then(|mut account_history| {
- account_history.add_account_token(new_account_token)
- }) {
- log::error!(
- "Unable to add an account into the account history: {}",
- e.display_chain()
- );
- }
- }
-
Box::new(future)
}
@@ -558,14 +558,11 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
fn get_account_history(&self, _: Self::Metadata) -> BoxFuture<Vec<AccountToken>, Error> {
log::debug!("get_account_history");
- Box::new(future::result(
- self.load_history()
- .map(|history| history.get_accounts().to_vec())
- .map_err(|error| {
- log::error!("Unable to get account history: {}", error.display_chain());
- Error::internal_error()
- }),
- ))
+ let (tx, rx) = sync::oneshot::channel();
+ let future = self
+ .send_command_to_daemon(ManagementCommand::GetAccountHistory(tx))
+ .and_then(|_| rx.map_err(|_| Error::internal_error()));
+ Box::new(future)
}
fn remove_account_from_history(
@@ -574,17 +571,14 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
account_token: AccountToken,
) -> BoxFuture<(), Error> {
log::debug!("remove_account_from_history");
- Box::new(future::result(
- self.load_history()
- .and_then(|mut history| history.remove_account_token(&account_token))
- .map_err(|error| {
- log::error!(
- "Unable to remove account from history: {}",
- error.display_chain()
- );
- Error::internal_error()
- }),
- ))
+ let (tx, rx) = sync::oneshot::channel();
+ let future = self
+ .send_command_to_daemon(ManagementCommand::RemoveAccountFromHistory(
+ tx,
+ account_token,
+ ))
+ .and_then(|_| rx.map_err(|_| Error::internal_error()));
+ Box::new(future)
}
fn set_openvpn_mssfix(&self, _: Self::Metadata, mssfix: Option<u16>) -> BoxFuture<(), Error> {
@@ -666,6 +660,39 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi
Box::new(future)
}
+ fn generate_wireguard_key(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
+ log::debug!("generate_wireguard_key");
+ let (tx, rx) = sync::oneshot::channel();
+ let future = self
+ .send_command_to_daemon(ManagementCommand::GenerateWireguardKey(tx))
+ .and_then(|_| {
+ rx.map_err(|_| Error::internal_error())
+ .and_then(|res| future::result(res.map_err(|_| Error::internal_error())))
+ });
+ Box::new(future)
+ }
+
+ fn get_wireguard_key(
+ &self,
+ _: Self::Metadata,
+ ) -> BoxFuture<Option<wireguard::PublicKey>, Error> {
+ log::debug!("get_wireguard_key");
+ let (tx, rx) = sync::oneshot::channel();
+ let future = self
+ .send_command_to_daemon(ManagementCommand::GetWireguardKey(tx))
+ .and_then(|_| rx.map_err(|_| Error::internal_error()));
+ Box::new(future)
+ }
+
+ fn verify_wireguard_key(&self, _: Self::Metadata) -> BoxFuture<bool, Error> {
+ log::debug!("verify_wireguard_key");
+ let (tx, rx) = sync::oneshot::channel();
+ let future = self
+ .send_command_to_daemon(ManagementCommand::VerifyWireguardKey(tx))
+ .and_then(|_| rx.map_err(|_| Error::internal_error()));
+ Box::new(future)
+ }
+
fn get_current_version(&self, _: Self::Metadata) -> BoxFuture<String, Error> {
log::debug!("get_current_version");
let (tx, rx) = sync::oneshot::channel();
diff --git a/mullvad-ipc-client/src/lib.rs b/mullvad-ipc-client/src/lib.rs
index b42dfb55fb..e207ad99fa 100644
--- a/mullvad-ipc-client/src/lib.rs
+++ b/mullvad-ipc-client/src/lib.rs
@@ -14,7 +14,10 @@ use mullvad_types::{
};
use serde::{Deserialize, Serialize};
use std::{path::Path, thread};
-use talpid_types::{net::openvpn, tunnel::TunnelStateTransition};
+use talpid_types::{
+ net::{openvpn, wireguard},
+ tunnel::TunnelStateTransition,
+};
pub use jsonrpc_client_core::{Error as RpcError, ErrorKind as RpcErrorKind};
@@ -190,6 +193,18 @@ impl DaemonRpcClient {
self.call("get_settings", &NO_ARGS)
}
+ pub fn generate_wireguard_key(&mut self) -> Result<()> {
+ self.call("generate_wireguard_key", &NO_ARGS)
+ }
+
+ pub fn get_wireguard_key(&mut self) -> Result<Option<wireguard::PublicKey>> {
+ self.call("get_wireguard_key", &NO_ARGS)
+ }
+
+ pub fn verify_wireguard_key(&mut self) -> Result<bool> {
+ self.call("verify_wireguard_key", &NO_ARGS)
+ }
+
pub fn get_version_info(&mut self) -> Result<AppVersionInfo> {
self.call("get_version_info", &NO_ARGS)
}
diff --git a/mullvad-rpc/Cargo.toml b/mullvad-rpc/Cargo.toml
index ff9596d10f..2f21283c58 100644
--- a/mullvad-rpc/Cargo.toml
+++ b/mullvad-rpc/Cargo.toml
@@ -22,6 +22,7 @@ tokio-openssl = "0.2"
log = "0.4"
mullvad-types = { path = "../mullvad-types" }
+talpid-types = { path = "../talpid-types" }
[dev-dependencies]
filetime = "0.1"
diff --git a/mullvad-rpc/src/lib.rs b/mullvad-rpc/src/lib.rs
index 93962dbdba..cde2de539c 100644
--- a/mullvad-rpc/src/lib.rs
+++ b/mullvad-rpc/src/lib.rs
@@ -21,6 +21,7 @@ use std::{
path::{Path, PathBuf},
time::Duration,
};
+use talpid_types::net::wireguard;
use tokio_core::reactor::Handle;
pub use jsonrpc_client_core::{Error, ErrorKind};
@@ -129,3 +130,16 @@ jsonrpc_client!(pub struct AppVersionProxy {
pub fn latest_app_version(&mut self) -> RpcRequest<version::LatestReleases>;
pub fn is_app_version_supported(&mut self, version: &version::AppVersion) -> RpcRequest<bool>;
});
+
+jsonrpc_client!(pub struct WireguardKeyProxy {
+ pub fn push_wg_key(
+ &mut self,
+ account_token: AccountToken,
+ public_key: wireguard::PublicKey
+ ) -> RpcRequest<mullvad_types::wireguard::AssociatedAddresses>;
+ pub fn check_wg_key(
+ &mut self,
+ account_token: AccountToken,
+ public_key: wireguard::PublicKey
+ ) -> RpcRequest<bool>;
+});
diff --git a/mullvad-types/Cargo.toml b/mullvad-types/Cargo.toml
index 8239696b9b..27c59acc0f 100644
--- a/mullvad-types/Cargo.toml
+++ b/mullvad-types/Cargo.toml
@@ -11,6 +11,7 @@ chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
error-chain = "0.12"
+ipnetwork = { git = "https://github.com/mullvad/ipnetwork", branch = "fix-deserialization" }
log = "0.4"
regex = "1"
lazy_static = "1.1.0"
diff --git a/mullvad-types/src/lib.rs b/mullvad-types/src/lib.rs
index 11e6ac4005..b2659911e4 100644
--- a/mullvad-types/src/lib.rs
+++ b/mullvad-types/src/lib.rs
@@ -18,6 +18,7 @@ pub mod relay_list;
pub mod settings;
pub mod states;
pub mod version;
+pub mod wireguard;
mod custom_tunnel;
pub use crate::custom_tunnel::*;
diff --git a/mullvad-types/src/wireguard.rs b/mullvad-types/src/wireguard.rs
new file mode 100644
index 0000000000..5fc0846557
--- /dev/null
+++ b/mullvad-types/src/wireguard.rs
@@ -0,0 +1,17 @@
+use serde::{Deserialize, Serialize};
+use talpid_types::net::wireguard;
+
+/// Contains account specific wireguard data
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct WireguardData {
+ pub private_key: wireguard::PrivateKey,
+ pub addresses: AssociatedAddresses,
+}
+
+/// Contains a pair of local link addresses that are paired with a specific wireguard
+/// public/private keypair.
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct AssociatedAddresses {
+ pub ipv4_address: ipnetwork::Ipv4Network,
+ pub ipv6_address: ipnetwork::Ipv6Network,
+}
diff --git a/talpid-types/Cargo.toml b/talpid-types/Cargo.toml
index 9ef4fa89b3..678a7832fc 100644
--- a/talpid-types/Cargo.toml
+++ b/talpid-types/Cargo.toml
@@ -12,3 +12,5 @@ ipnetwork = "0.14"
base64 = "0.10"
hex = "0.3"
error-chain = "0.12"
+x25519-dalek = { version = "0.4.5", features = [ "std", "u64_backend" ], default-features = false }
+rand = "0.6"
diff --git a/talpid-types/src/net/wireguard.rs b/talpid-types/src/net/wireguard.rs
index d24ffee0e3..fe596fe475 100644
--- a/talpid-types/src/net/wireguard.rs
+++ b/talpid-types/src/net/wireguard.rs
@@ -6,6 +6,8 @@ use std::{
net::{IpAddr, SocketAddr},
};
+use rand::RngCore;
+
#[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)]
/// Wireguard tunnel parameters
@@ -67,10 +69,32 @@ impl PrivateKey {
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
+
+ /// Normalizing a private key as per the specification - https://cr.yp.to/ecdh.html
+ fn normalize_key(bytes: &mut [u8; 32]) {
+ bytes[0] &= 248;
+ bytes[31] &= 127;
+ bytes[31] |= 64;
+ }
+
+ pub fn new_from_random() -> Result<Self, rand::Error> {
+ let mut bytes = [0u8; 32];
+ rand::rngs::OsRng::new()?.fill_bytes(&mut bytes);
+ Ok(Self::from(bytes))
+ }
+
+ /// Generate public key from private key
+ pub fn public_key(&self) -> PublicKey {
+ PublicKey::from(x25519_dalek::x25519(
+ self.0,
+ x25519_dalek::X25519_BASEPOINT_BYTES,
+ ))
+ }
}
impl From<[u8; 32]> for PrivateKey {
- fn from(private_key: [u8; 32]) -> PrivateKey {
+ fn from(mut private_key: [u8; 32]) -> PrivateKey {
+ Self::normalize_key(&mut private_key);
PrivateKey(private_key)
}
}