diff options
| author | David Lönnhager <david.l@mullvad.net> | 2022-05-02 16:17:19 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2022-05-05 10:08:29 +0200 |
| commit | 79597f626eefee882e468919b1c0e2123b612ef2 (patch) | |
| tree | 930f8e9b6b96d90081555ce9dbccdcccb2f437b9 | |
| parent | 24742485bd400e84ba7279b992867f0d9de9f4b1 (diff) | |
| download | mullvadvpn-79597f626eefee882e468919b1c0e2123b612ef2.tar.xz mullvadvpn-79597f626eefee882e468919b1c0e2123b612ef2.zip | |
Hide private device type in `mullvad-daemon`
| -rw-r--r-- | android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountAndDevice.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceConfig.kt) | 4 | ||||
| -rw-r--r-- | android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt | 2 | ||||
| -rw-r--r-- | android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt | 6 | ||||
| -rw-r--r-- | android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt | 6 | ||||
| -rw-r--r-- | mullvad-daemon/src/device/api.rs | 18 | ||||
| -rw-r--r-- | mullvad-daemon/src/device/mod.rs | 231 | ||||
| -rw-r--r-- | mullvad-daemon/src/device/service.rs | 52 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 59 | ||||
| -rw-r--r-- | mullvad-daemon/src/migrations/device.rs | 83 | ||||
| -rw-r--r-- | mullvad-jni/src/classes.rs | 2 | ||||
| -rw-r--r-- | mullvad-jni/src/daemon_interface.rs | 4 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types.rs | 8 | ||||
| -rw-r--r-- | mullvad-setup/src/main.rs | 2 | ||||
| -rw-r--r-- | mullvad-types/src/device.rs | 68 |
14 files changed, 298 insertions, 247 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceConfig.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountAndDevice.kt index b8f5664e30..1a4ed323b9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceConfig.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountAndDevice.kt @@ -4,7 +4,7 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class DeviceConfig( - val token: String, +data class AccountAndDevice( + val account_token: String, val device: Device ) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt index 24703e7066..1f2b68fbe5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt @@ -5,6 +5,6 @@ import kotlinx.parcelize.Parcelize @Parcelize data class DeviceEvent( - val device: DeviceConfig?, + val device: AccountAndDevice?, val remote: Boolean ) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt index a4fd52cabb..8903cae6a9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt @@ -8,7 +8,7 @@ sealed class DeviceState : Parcelable { object InitialState : DeviceState() @Parcelize - data class DeviceRegistered(val deviceConfig: DeviceConfig) : DeviceState() + data class DeviceRegistered(val deviceConfig: AccountAndDevice) : DeviceState() @Parcelize object DeviceNotRegistered : DeviceState() @@ -22,11 +22,11 @@ sealed class DeviceState : Parcelable { } fun token(): String? { - return (this as? DeviceRegistered)?.deviceConfig?.token + return (this as? DeviceRegistered)?.deviceConfig?.account_token } companion object { - fun fromDeviceConfig(deviceConfig: DeviceConfig?): DeviceState { + fun fromDeviceConfig(deviceConfig: AccountAndDevice?): DeviceState { return deviceConfig?.let { DeviceRegistered(it) } ?: DeviceNotRegistered } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt index 2aefe71a91..c7c2bb1e95 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadDaemon.kt @@ -3,9 +3,9 @@ package net.mullvad.mullvadvpn.service import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow +import net.mullvad.mullvadvpn.model.AccountAndDevice import net.mullvad.mullvadvpn.model.AppVersionInfo import net.mullvad.mullvadvpn.model.Device -import net.mullvad.mullvadvpn.model.DeviceConfig import net.mullvad.mullvadvpn.model.DeviceEvent import net.mullvad.mullvadvpn.model.DeviceState import net.mullvad.mullvadvpn.model.DnsOptions @@ -126,7 +126,7 @@ class MullvadDaemon(vpnService: MullvadVpnService) { return listDevices(daemonInterfaceAddress, accountToken) } - fun getDevice(): DeviceConfig? = getDevice(daemonInterfaceAddress) + fun getDevice(): AccountAndDevice? = getDevice(daemonInterfaceAddress) fun updateDevice() = updateDevice(daemonInterfaceAddress) @@ -216,7 +216,7 @@ class MullvadDaemon(vpnService: MullvadVpnService) { accountToken: String? ): List<Device>? - private external fun getDevice(daemonInterfaceAddress: Long): DeviceConfig? + private external fun getDevice(daemonInterfaceAddress: Long): AccountAndDevice? private external fun updateDevice(daemonInterfaceAddress: Long) private external fun removeDevice( daemonInterfaceAddress: Long, diff --git a/mullvad-daemon/src/device/api.rs b/mullvad-daemon/src/device/api.rs index 9005c41364..cee0186987 100644 --- a/mullvad-daemon/src/device/api.rs +++ b/mullvad-daemon/src/device/api.rs @@ -1,13 +1,11 @@ use std::pin::Pin; use futures::{future::FusedFuture, Future}; -use mullvad_types::{ - device::{Device, DeviceData}, - wireguard::WireguardData, -}; +use mullvad_types::{device::Device, wireguard::WireguardData}; -use super::{Error, ResponseTx}; -pub struct CurrentApiCall { +use super::{Error, PrivateAccountAndDevice, ResponseTx}; + +pub(crate) struct CurrentApiCall { current_call: Option<Call>, } @@ -20,7 +18,7 @@ impl CurrentApiCall { self.current_call = None; } - pub fn set_login(&mut self, login: ApiCall<DeviceData>, tx: ResponseTx<()>) { + pub fn set_login(&mut self, login: ApiCall<PrivateAccountAndDevice>, tx: ResponseTx<()>) { self.current_call = Some(Call::Login(login, Some(tx))); } @@ -89,7 +87,7 @@ impl FusedFuture for CurrentApiCall { type ApiCall<T> = Pin<Box<dyn Future<Output = Result<T, Error>> + Send>>; enum Call { - Login(ApiCall<DeviceData>, Option<ResponseTx<()>>), + Login(ApiCall<PrivateAccountAndDevice>, Option<ResponseTx<()>>), TimerKeyRotation(ApiCall<WireguardData>), OneshotKeyRotation(ApiCall<WireguardData>), Validation(ApiCall<Device>), @@ -119,8 +117,8 @@ impl futures::Future for Call { } } -pub enum ApiResult { - Login(Result<DeviceData, Error>, ResponseTx<()>), +pub(crate) enum ApiResult { + Login(Result<PrivateAccountAndDevice, Error>, ResponseTx<()>), Rotation(Result<WireguardData, Error>), Validation(Result<Device, Error>), } diff --git a/mullvad-daemon/src/device/mod.rs b/mullvad-daemon/src/device/mod.rs index 158330f15a..753c963cca 100644 --- a/mullvad-daemon/src/device/mod.rs +++ b/mullvad-daemon/src/device/mod.rs @@ -7,8 +7,8 @@ use futures::{ use mullvad_api::{availability::ApiAvailabilityHandle, rest}; use mullvad_types::{ account::AccountToken, - device::{Device, DeviceData, DeviceEvent, InnerDevice}, - wireguard::{RotationInterval, WireguardData}, + device::{AccountAndDevice, Device, DeviceEvent, DeviceId, DeviceName, DevicePort}, + wireguard::{self, RotationInterval, WireguardData}, }; use std::{ future::Future, @@ -28,7 +28,7 @@ use tokio::{ mod api; mod service; -pub use service::{spawn_account_service, AccountService, DeviceService}; +pub(crate) use service::{AccountService, DeviceService}; /// File that used to store account and device data. const DEVICE_CACHE_FILENAME: &str = "device.json"; @@ -70,39 +70,112 @@ pub enum Error { AccountManagerDown, } +/// Same as [PrivateDevice] but also contains the associated account token. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +pub struct PrivateAccountAndDevice { + pub account_token: AccountToken, + pub device: PrivateDevice, +} + +impl From<PrivateAccountAndDevice> for AccountAndDevice { + fn from(config: PrivateAccountAndDevice) -> Self { + AccountAndDevice { + account_token: config.account_token, + device: Device::from(config.device), + } + } +} + +/// Device type that contains private data. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +pub struct PrivateDevice { + pub id: DeviceId, + pub name: DeviceName, + pub wg_data: wireguard::WireguardData, + pub ports: Vec<DevicePort>, +} + +impl PrivateDevice { + /// Construct a private device from a `WireguardData` and a `Device`. Fails if the pubkey of + /// `device` does not match that of `wg_data`. + pub fn try_from_device( + device: Device, + wg_data: wireguard::WireguardData, + ) -> Result<Self, Error> { + if device.pubkey != wg_data.private_key.public_key() { + return Err(Error::InvalidDevice); + } + Ok(Self { + id: device.id, + name: device.name, + wg_data, + ports: device.ports, + }) + } + + /// Update all device details that are present in both types. Fails if the pubkey of `device` + /// does not match that of `wg_data`. + fn update(&mut self, device: Device) -> Result<(), Error> { + if device.pubkey != self.wg_data.private_key.public_key() { + return Err(Error::InvalidDevice); + } + self.id = device.id; + self.ports = device.ports; + self.name = device.name; + Ok(()) + } +} + +impl From<PrivateDevice> for Device { + fn from(device: PrivateDevice) -> Self { + Device { + id: device.id, + ports: device.ports, + pubkey: device.wg_data.private_key.public_key(), + name: device.name, + } + } +} + #[derive(Clone)] -pub(crate) enum InnerDeviceEvent { +pub(crate) enum PrivateDeviceEvent { /// The device was removed due to user (or daemon) action. Logout, /// Logged in to a new device. - Login(DeviceData), + Login(PrivateAccountAndDevice), /// The device was updated remotely, but not its key. - Updated(DeviceData), + Updated(PrivateAccountAndDevice), /// The key was rotated. - RotatedKey(DeviceData), + RotatedKey(PrivateAccountAndDevice), /// Device was removed because it was not found remotely. Revoked, } -impl From<InnerDeviceEvent> for DeviceEvent { - fn from(event: InnerDeviceEvent) -> DeviceEvent { +impl From<PrivateDeviceEvent> for DeviceEvent { + fn from(event: PrivateDeviceEvent) -> DeviceEvent { match event { - InnerDeviceEvent::Logout => DeviceEvent::revoke(false), - InnerDeviceEvent::Login(data) => DeviceEvent::from_device(data, false), - InnerDeviceEvent::Updated(data) => DeviceEvent::from_device(data, true), - InnerDeviceEvent::RotatedKey(data) => DeviceEvent::from_device(data, false), - InnerDeviceEvent::Revoked => DeviceEvent::revoke(true), + PrivateDeviceEvent::Logout => DeviceEvent::revoke(false), + PrivateDeviceEvent::Login(config) => { + DeviceEvent::from_device(AccountAndDevice::from(config), false) + } + PrivateDeviceEvent::Updated(config) => { + DeviceEvent::from_device(AccountAndDevice::from(config), true) + } + PrivateDeviceEvent::RotatedKey(config) => { + DeviceEvent::from_device(AccountAndDevice::from(config), false) + } + PrivateDeviceEvent::Revoked => DeviceEvent::revoke(true), } } } -impl InnerDeviceEvent { - fn data(&self) -> Option<&DeviceData> { +impl PrivateDeviceEvent { + fn data(&self) -> Option<&PrivateAccountAndDevice> { match self { - InnerDeviceEvent::Login(data) => Some(&data), - InnerDeviceEvent::Updated(data) => Some(&data), - InnerDeviceEvent::RotatedKey(data) => Some(&data), - InnerDeviceEvent::Logout | InnerDeviceEvent::Revoked => None, + PrivateDeviceEvent::Login(config) => Some(config), + PrivateDeviceEvent::Updated(config) => Some(config), + PrivateDeviceEvent::RotatedKey(config) => Some(config), + PrivateDeviceEvent::Logout | PrivateDeviceEvent::Revoked => None, } } } @@ -122,13 +195,13 @@ type ResponseTx<T> = oneshot::Sender<Result<T, Error>>; enum AccountManagerCommand { Login(AccountToken, ResponseTx<()>), Logout(ResponseTx<()>), - SetData(DeviceData, ResponseTx<()>), - GetData(ResponseTx<Option<DeviceData>>), - GetDataAfterLogin(ResponseTx<Option<DeviceData>>), + SetData(PrivateAccountAndDevice, ResponseTx<()>), + GetData(ResponseTx<Option<PrivateAccountAndDevice>>), + GetDataAfterLogin(ResponseTx<Option<PrivateAccountAndDevice>>), RotateKey(ResponseTx<()>), SetRotationInterval(RotationInterval, ResponseTx<()>), ValidateDevice(ResponseTx<()>), - ReceiveEvents(Box<dyn Sender<InnerDeviceEvent> + Send>, ResponseTx<()>), + ReceiveEvents(Box<dyn Sender<PrivateDeviceEvent> + Send>, ResponseTx<()>), Shutdown(oneshot::Sender<()>), } @@ -150,17 +223,17 @@ impl AccountManagerHandle { .await } - pub async fn set(&self, data: DeviceData) -> Result<(), Error> { + pub async fn set(&self, data: PrivateAccountAndDevice) -> Result<(), Error> { self.send_command(|tx| AccountManagerCommand::SetData(data, tx)) .await } - pub async fn data(&self) -> Result<Option<DeviceData>, Error> { + pub async fn data(&self) -> Result<Option<PrivateAccountAndDevice>, Error> { self.send_command(|tx| AccountManagerCommand::GetData(tx)) .await } - pub async fn data_after_login(&self) -> Result<Option<DeviceData>, Error> { + pub async fn data_after_login(&self) -> Result<Option<PrivateAccountAndDevice>, Error> { self.send_command(|tx| AccountManagerCommand::GetDataAfterLogin(tx)) .await } @@ -182,7 +255,7 @@ impl AccountManagerHandle { pub async fn receive_events( &self, - events_tx: impl Sender<InnerDeviceEvent> + Send + 'static, + events_tx: impl Sender<PrivateDeviceEvent> + Send + 'static, ) -> Result<(), Error> { self.send_command(|tx| { AccountManagerCommand::ReceiveEvents(Box::new(events_tx) as Box<_>, tx) @@ -213,13 +286,13 @@ impl AccountManagerHandle { pub(crate) struct AccountManager { cacher: DeviceCacher, device_service: DeviceService, - data: Option<DeviceData>, + data: Option<PrivateAccountAndDevice>, rotation_interval: RotationInterval, - listeners: Vec<Box<dyn Sender<InnerDeviceEvent> + Send>>, + listeners: Vec<Box<dyn Sender<PrivateDeviceEvent> + Send>>, last_validation: Option<SystemTime>, validation_requests: Vec<ResponseTx<()>>, rotation_requests: Vec<ResponseTx<()>>, - data_requests: Vec<ResponseTx<Option<DeviceData>>>, + data_requests: Vec<ResponseTx<Option<PrivateAccountAndDevice>>>, } impl AccountManager { @@ -230,9 +303,9 @@ impl AccountManager { initial_rotation_interval: RotationInterval, ) -> Result<AccountManagerHandle, Error> { let (cacher, data) = DeviceCacher::new(settings_dir).await?; - let token = data.as_ref().map(|state| state.token.clone()); + let token = data.as_ref().map(|state| state.account_token.clone()); let account_service = - spawn_account_service(rest_handle.clone(), token, api_availability.clone()); + service::spawn_account_service(rest_handle.clone(), token, api_availability.clone()); let (cmd_tx, cmd_rx) = mpsc::unbounded(); @@ -284,7 +357,7 @@ impl AccountManager { self.logout(tx).await; } Some(AccountManagerCommand::SetData(data, tx)) => { - let _ = tx.send(self.set(InnerDeviceEvent::Login(data)).await); + let _ = tx.send(self.set(PrivateDeviceEvent::Login(data)).await); } Some(AccountManagerCommand::GetData(tx)) => { let _ = tx.send(Ok(self.data.clone())); @@ -393,10 +466,11 @@ impl AccountManager { async fn consume_login( &mut self, - device_response: Result<DeviceData, Error>, + device_response: Result<PrivateAccountAndDevice, Error>, tx: ResponseTx<()>, ) { - let _ = tx.send(async { self.set(InnerDeviceEvent::Login(device_response?)).await }.await); + let _ = + tx.send(async { self.set(PrivateDeviceEvent::Login(device_response?)).await }.await); let data = self.data.clone(); Self::drain_requests(&mut self.data_requests, || Ok(data.clone())); } @@ -406,7 +480,7 @@ impl AccountManager { response: Result<Device, Error>, api_call: &mut api::CurrentApiCall, ) { - let current_data = match self.data.as_ref() { + let current_config = match self.data.as_ref() { Some(data) => data, None => { panic!("Received a validation response whilst having no device data"); @@ -414,13 +488,14 @@ impl AccountManager { }; match response { - Ok(new_device_data) => { - let current_pubkey = current_data.wg_data.private_key.public_key(); - if new_device_data.pubkey == current_pubkey { - let new_data = DeviceData { - device: InnerDevice::from(new_device_data), - ..current_data.clone() - }; + Ok(new_device) => { + let current_pubkey = current_config.device.wg_data.private_key.public_key(); + if new_device.pubkey == current_pubkey { + let mut new_data = current_config.clone(); + new_data + .device + .update(new_device) + .expect("pubkey must match privkey"); if Some(&new_data) != self.data.as_ref() { log::debug!("Updating data for the current device"); @@ -428,7 +503,7 @@ impl AccountManager { log::debug!("The current device is still valid"); } - match self.set(InnerDeviceEvent::Updated(new_data)).await { + match self.set(PrivateDeviceEvent::Updated(new_data)).await { Ok(_) => { Self::drain_requests(&mut self.validation_requests, || Ok(())); } @@ -460,10 +535,10 @@ impl AccountManager { } if !self.rotation_requests.is_empty() || !self.validation_requests.is_empty() { - if let Some(updated_data) = self.data.as_ref() { + if let Some(updated_config) = self.data.as_ref() { let device_service = self.device_service.clone(); - let token = updated_data.token.clone(); - let device_id = updated_data.device.id.clone(); + let token = updated_config.account_token.clone(); + let device_id = updated_config.device.id.clone(); api_call.set_oneshot_rotation(Box::pin(async move { device_service.rotate_key(token, device_id).await })); @@ -472,15 +547,15 @@ impl AccountManager { } async fn consume_rotation_result(&mut self, api_result: Result<WireguardData, Error>) { - let mut device_data = self + let mut config = self .data .clone() .expect("Received a key rotation result whilst having no data"); match api_result { Ok(wg_data) => { - device_data.wg_data = wg_data; - match self.set(InnerDeviceEvent::RotatedKey(device_data)).await { + config.device.wg_data = wg_data; + match self.set(PrivateDeviceEvent::RotatedKey(config)).await { Ok(_) => { Self::drain_requests(&mut self.rotation_requests, || Ok(())); @@ -522,12 +597,12 @@ impl AccountManager { fn spawn_timed_key_rotation( &self, ) -> Option<impl Future<Output = Result<WireguardData, Error>> + Send + 'static> { - let data = self.data.as_ref()?; - let key_rotation_timer = self.key_rotation_timer(data.wg_data.created); + let config = self.data.as_ref()?; + let key_rotation_timer = self.key_rotation_timer(config.device.wg_data.created); let device_service = self.device_service.clone(); - let account_token = data.token.clone(); - let device_id = data.device.id.clone(); + let account_token = config.account_token.clone(); + let device_id = config.device.id.clone(); Some(async move { key_rotation_timer.await; @@ -550,7 +625,7 @@ impl AccountManager { Self::drain_requests(&mut self.rotation_requests, || Err(err_constructor())); self.listeners - .retain(|listener| listener.send(InnerDeviceEvent::Revoked).is_ok()); + .retain(|listener| listener.send(PrivateDeviceEvent::Revoked).is_ok()); } async fn logout(&mut self, tx: ResponseTx<()>) { @@ -565,12 +640,12 @@ impl AccountManager { } // Cannot panic: `data.is_none() == false`. - let old_data = self.data.take().unwrap(); + let old_config = self.data.take().unwrap(); self.listeners - .retain(|listener| listener.send(InnerDeviceEvent::Logout).is_ok()); + .retain(|listener| listener.send(PrivateDeviceEvent::Logout).is_ok()); - let logout_call = tokio::spawn(Box::pin(self.logout_api_call(old_data))); + let logout_call = tokio::spawn(Box::pin(self.logout_api_call(old_config))); tokio::spawn(async move { let _response = tokio::time::timeout(LOGOUT_TIMEOUT, logout_call).await; @@ -578,12 +653,12 @@ impl AccountManager { }); } - fn logout_api_call(&self, data: DeviceData) -> impl Future<Output = ()> + 'static { + fn logout_api_call(&self, data: PrivateAccountAndDevice) -> impl Future<Output = ()> + 'static { let service = self.device_service.clone(); async move { if let Err(error) = service - .remove_device_with_backoff(data.token, data.device.id) + .remove_device_with_backoff(data.account_token, data.device.id) .await { log::error!( @@ -594,7 +669,7 @@ impl AccountManager { } } - async fn set(&mut self, event: InnerDeviceEvent) -> Result<(), Error> { + async fn set(&mut self, event: PrivateDeviceEvent) -> Result<(), Error> { let data = event.data(); if data == self.data.as_ref() { return Ok(()); @@ -603,9 +678,9 @@ impl AccountManager { self.cacher.write(data).await?; self.last_validation = None; - if let Some(old_data) = self.data.take() { - if data.as_ref().map(|d| &d.device.id) != Some(&old_data.device.id) { - tokio::spawn(self.logout_api_call(old_data)); + if let Some(old_config) = self.data.take() { + if data.as_ref().map(|d| &d.device.id) != Some(&old_config.device.id) { + tokio::spawn(self.logout_api_call(old_config)); } } @@ -622,7 +697,11 @@ impl AccountManager { ) -> Result<impl Future<Output = Result<WireguardData, Error>>, Error> { let data = self.data.clone().ok_or(Error::NoDevice)?; let device_service = self.device_service.clone(); - Ok(async move { device_service.rotate_key(data.token, data.device.id).await }) + Ok(async move { + device_service + .rotate_key(data.account_token, data.device.id) + .await + }) } fn key_rotation_timer(&self, key_created: DateTime<Utc>) -> impl Future<Output = ()> + 'static { @@ -652,19 +731,19 @@ impl AccountManager { } } - fn fetch_device_data( + fn fetch_device_config( &self, - old_data: &DeviceData, + old_config: &PrivateAccountAndDevice, ) -> impl Future<Output = Result<Device, Error>> { let device_service = self.device_service.clone(); - let account_token = old_data.token.clone(); - let device_id = old_data.device.id.clone(); + let account_token = old_config.account_token.clone(); + let device_id = old_config.device.id.clone(); async move { device_service.get(account_token, device_id).await } } fn validation_call(&self) -> Result<impl Future<Output = Result<Device, Error>>, Error> { - let old_data = self.data.as_ref().ok_or(Error::NoDevice)?; - let device_request = self.fetch_device_data(old_data); + let old_config = self.data.as_ref().ok_or(Error::NoDevice)?; + let device_request = self.fetch_device_config(old_config); Ok(async move { device_request.await }) } @@ -698,7 +777,9 @@ pub struct DeviceCacher { } impl DeviceCacher { - pub async fn new(settings_dir: &Path) -> Result<(DeviceCacher, Option<DeviceData>), Error> { + pub async fn new( + settings_dir: &Path, + ) -> Result<(DeviceCacher, Option<PrivateAccountAndDevice>), Error> { let path = settings_dir.join(DEVICE_CACHE_FILENAME); let cache_exists = path.is_file(); @@ -709,7 +790,7 @@ impl DeviceCacher { .open(&path) .await?; - let device: Option<DeviceData> = if cache_exists { + let device: Option<PrivateAccountAndDevice> = if cache_exists { let mut reader = io::BufReader::new(&mut file); let mut buffer = String::new(); reader.read_to_string(&mut buffer).await?; @@ -753,7 +834,7 @@ impl DeviceCacher { options } - pub async fn write(&mut self, device: Option<&DeviceData>) -> Result<(), Error> { + pub async fn write(&mut self, device: Option<&PrivateAccountAndDevice>) -> Result<(), Error> { let data = serde_json::to_vec_pretty(&device).unwrap(); self.file.get_mut().set_len(0).await?; diff --git a/mullvad-daemon/src/device/service.rs b/mullvad-daemon/src/device/service.rs index 7f39bfc145..f511a35f67 100644 --- a/mullvad-daemon/src/device/service.rs +++ b/mullvad-daemon/src/device/service.rs @@ -4,12 +4,12 @@ use chrono::{DateTime, Utc}; use futures::future::{abortable, AbortHandle}; use mullvad_types::{ account::{AccountToken, VoucherSubmission}, - device::{Device, DeviceData, DeviceId, InnerDevice}, + device::{Device, DeviceId}, wireguard::WireguardData, }; use talpid_types::net::wireguard::PrivateKey; -use super::Error; +use super::{Error, PrivateAccountAndDevice, PrivateDevice}; use mullvad_api::{ availability::ApiAvailabilityHandle, rest::{self, Error as RestError, MullvadRestHandle}, @@ -42,14 +42,14 @@ impl DeviceService { /// Generate a new device for a given token pub fn generate_for_account( &self, - token: AccountToken, - ) -> impl Future<Output = Result<DeviceData, Error>> + Send { + account_token: AccountToken, + ) -> impl Future<Output = Result<PrivateAccountAndDevice, Error>> + Send { let private_key = PrivateKey::new_from_random(); let pubkey = private_key.public_key(); let proxy = self.proxy.clone(); let api_handle = self.api_availability.clone(); - let token_copy = token.clone(); + let token_copy = account_token.clone(); async move { let (device, addresses) = retry_future_n( move || proxy.create(token_copy.clone(), pubkey.clone()), @@ -60,28 +60,30 @@ impl DeviceService { .await .map_err(map_rest_error)?; - Ok(DeviceData { - token, - device: InnerDevice::from(device), - wg_data: WireguardData { - private_key, - addresses, - created: Utc::now(), - }, + Ok(PrivateAccountAndDevice { + account_token, + device: PrivateDevice::try_from_device( + device, + WireguardData { + private_key, + addresses, + created: Utc::now(), + }, + )?, }) } } pub async fn generate_for_account_with_backoff( &self, - token: AccountToken, - ) -> Result<DeviceData, Error> { + account_token: AccountToken, + ) -> Result<PrivateAccountAndDevice, Error> { let private_key = PrivateKey::new_from_random(); let pubkey = private_key.public_key(); let proxy = self.proxy.clone(); let api_handle = self.api_availability.clone(); - let token_copy = token.clone(); + let token_copy = account_token.clone(); let (device, addresses) = retry_future( move || api_handle.when_online(proxy.create(token_copy.clone(), pubkey.clone())), should_retry_backoff, @@ -90,14 +92,16 @@ impl DeviceService { .await .map_err(map_rest_error)?; - Ok(DeviceData { - token, - device: InnerDevice::from(device), - wg_data: WireguardData { - private_key, - addresses, - created: Utc::now(), - }, + Ok(PrivateAccountAndDevice { + account_token, + device: PrivateDevice::try_from_device( + device, + WireguardData { + private_key, + addresses, + created: Utc::now(), + }, + )?, }) } diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index eb674b5763..04300018fd 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -25,7 +25,7 @@ pub mod version; mod version_check; use crate::target_state::PersistentTargetState; -use device::InnerDeviceEvent; +use device::{PrivateAccountAndDevice, PrivateDeviceEvent}; use futures::{ channel::{mpsc, oneshot}, future::{abortable, AbortHandle, Future}, @@ -38,7 +38,7 @@ use mullvad_relay_selector::{ }; use mullvad_types::{ account::{AccountData, AccountToken, VoucherSubmission}, - device::{Device, DeviceConfig, DeviceData, DeviceEvent, DeviceId, RemoveDeviceEvent}, + device::{AccountAndDevice, Device, DeviceEvent, DeviceId, RemoveDeviceEvent}, endpoint::MullvadEndpoint, location::GeoIpLocation, relay_constraints::{BridgeSettings, BridgeState, ObfuscationSettings, RelaySettingsUpdate}, @@ -236,7 +236,7 @@ pub enum DaemonCommand { /// Log out of the current account and remove the device, if they exist. LogoutAccount(ResponseTx<(), Error>), /// Return the current device configuration, if there is one. - GetDevice(ResponseTx<Option<DeviceConfig>, Error>), + GetDevice(ResponseTx<Option<AccountAndDevice>, Error>), /// Update/check the current device, if there is one. UpdateDevice(ResponseTx<(), Error>), /// Return all the devices for a given account token. @@ -340,9 +340,9 @@ pub(crate) enum InternalDaemonEvent { /// The background job fetching new `AppVersionInfo`s got a new info object. NewAppVersionInfo(AppVersionInfo), /// Sent when a device is updated in any way (key rotation, login, logout, etc.). - DeviceEvent(InnerDeviceEvent), + DeviceEvent(PrivateDeviceEvent), /// Handles updates from versions without devices. - DeviceMigrationEvent(Result<DeviceData, device::Error>), + DeviceMigrationEvent(Result<PrivateAccountAndDevice, device::Error>), /// The split tunnel paths or state were updated. #[cfg(target_os = "windows")] ExcludedPathsEvent(ExcludedPathsUpdate, oneshot::Sender<Result<(), Error>>), @@ -372,8 +372,8 @@ impl From<AppVersionInfo> for InternalDaemonEvent { } } -impl From<InnerDeviceEvent> for InternalDaemonEvent { - fn from(event: InnerDeviceEvent) -> Self { +impl From<PrivateDeviceEvent> for InternalDaemonEvent { + fn from(event: PrivateDeviceEvent) -> Self { InternalDaemonEvent::DeviceEvent(event) } } @@ -665,7 +665,7 @@ where let account_history = account_history::AccountHistory::new( &settings_dir, - data.as_ref().map(|device| device.token.clone()), + data.as_ref().map(|device| device.account_token.clone()), ) .await .map_err(Error::LoadAccountHistory)?; @@ -1032,7 +1032,7 @@ where endpoint: MullvadEndpoint, bridge: Option<SelectedBridge>, obfuscator: Option<SelectedObfuscator>, - device: DeviceData, + device: PrivateAccountAndDevice, ) -> Result<TunnelParameters, Error> { let tunnel_options = self.settings.tunnel_options.clone(); match endpoint { @@ -1052,7 +1052,11 @@ where }); Ok(openvpn::TunnelParameters { - config: openvpn::ConnectionConfig::new(endpoint, device.token, "-".to_string()), + config: openvpn::ConnectionConfig::new( + endpoint, + device.account_token, + "-".to_string(), + ), options: tunnel_options.openvpn, generic_options: tunnel_options.generic, proxy: bridge_settings, @@ -1065,10 +1069,10 @@ where } MullvadEndpoint::Wireguard(endpoint) => { let tunnel = wireguard::TunnelConfig { - private_key: device.wg_data.private_key, + private_key: device.device.wg_data.private_key, addresses: vec![ - device.wg_data.addresses.ipv4_address.ip().into(), - device.wg_data.addresses.ipv6_address.ip().into(), + device.device.wg_data.addresses.ipv4_address.ip().into(), + device.device.wg_data.addresses.ipv6_address.ip().into(), ], }; @@ -1217,10 +1221,10 @@ where self.event_listener.notify_app_version(app_version_info); } - async fn handle_device_event(&mut self, event: InnerDeviceEvent) { + async fn handle_device_event(&mut self, event: PrivateDeviceEvent) { match &event { - InnerDeviceEvent::Login(device) => { - if let Err(error) = self.account_history.set(device.token.clone()).await { + PrivateDeviceEvent::Login(device) => { + if let Err(error) = self.account_history.set(device.account_token.clone()).await { log::error!( "{}", error.display_chain_with_msg("Failed to update account history") @@ -1231,18 +1235,18 @@ where self.reconnect_tunnel(); } } - InnerDeviceEvent::Logout => { + PrivateDeviceEvent::Logout => { log::info!("Disconnecting because account token was cleared"); self.set_target_state(TargetState::Unsecured).await; } - InnerDeviceEvent::Revoked => { + PrivateDeviceEvent::Revoked => { // If we're currently in a secured state, reconnect to make sure we immediately // enter the error state. if *self.target_state == TargetState::Secured { self.connect_tunnel(); } } - InnerDeviceEvent::RotatedKey(_) => { + PrivateDeviceEvent::RotatedKey(_) => { if let Some(TunnelType::Wireguard) = self.get_target_tunnel_type() { self.schedule_reconnect(WG_RECONNECT_DELAY); } @@ -1253,7 +1257,10 @@ where .notify_device_event(DeviceEvent::from(event)); } - async fn handle_device_migration_event(&mut self, result: Result<DeviceData, device::Error>) { + async fn handle_device_migration_event( + &mut self, + result: Result<PrivateAccountAndDevice, device::Error>, + ) { let account_manager = self.account_manager.clone(); let event_listener = self.event_listener.clone(); tokio::spawn(async move { @@ -1479,7 +1486,7 @@ where let future = self .account_manager .account_service - .get_www_auth_token(device.token); + .get_www_auth_token(device.account_token); tokio::spawn(async { Self::oneshot_send( tx, @@ -1507,7 +1514,7 @@ where Self::oneshot_send( tx, account - .submit_voucher(device.token, voucher) + .submit_voucher(device.account_token, voucher) .await .map_err(Error::RestError), "submit_voucher response", @@ -1552,7 +1559,7 @@ where }); } - async fn on_get_device(&mut self, tx: ResponseTx<Option<DeviceConfig>, Error>) { + async fn on_get_device(&mut self, tx: ResponseTx<Option<AccountAndDevice>, Error>) { let account_manager = self.account_manager.clone(); tokio::spawn(async move { Self::oneshot_send( @@ -1561,7 +1568,7 @@ where .data() .await .unwrap_or(None) - .map(DeviceConfig::from)), + .map(AccountAndDevice::from)), "get_device response", ); }); @@ -2290,8 +2297,8 @@ where } async fn on_get_wireguard_key(&self, tx: ResponseTx<Option<PublicKey>, Error>) { - let result = if let Ok(Some(device)) = self.account_manager.data().await { - Ok(Some(device.wg_data.get_public_key())) + let result = if let Ok(Some(config)) = self.account_manager.data().await { + Ok(Some(config.device.wg_data.get_public_key())) } else { Err(Error::NoAccountToken) }; diff --git a/mullvad-daemon/src/migrations/device.rs b/mullvad-daemon/src/migrations/device.rs index 6420d5df51..ff6cb16379 100644 --- a/mullvad-daemon/src/migrations/device.rs +++ b/mullvad-daemon/src/migrations/device.rs @@ -8,14 +8,10 @@ use super::{v5::MigrationData, MigrationComplete}; use crate::{ - device::{self, DeviceService}, + device::{self, DeviceService, PrivateAccountAndDevice, PrivateDevice}, DaemonEventSender, InternalDaemonEvent, }; -use mullvad_types::{ - account::AccountToken, - device::{DeviceData, InnerDevice}, - wireguard::WireguardData, -}; +use mullvad_types::{account::AccountToken, wireguard::WireguardData}; use std::time::Duration; use talpid_core::mpsc::Sender; use talpid_types::ErrorExt; @@ -61,51 +57,56 @@ pub(crate) fn generate_device( async fn cache_from_wireguard_key( service: DeviceService, - token: AccountToken, + account_token: AccountToken, wg_data: WireguardData, -) -> Result<DeviceData, device::Error> { - let devices = timeout(TIMEOUT, service.list_devices_with_backoff(token.clone())) - .await - .map_err(|_error| { - log::error!("Failed to enumerate devices for account: timed out"); - device::Error::Cancelled - })? - .map_err(|error| { - log::error!( - "{}", - error.display_chain_with_msg("Failed to enumerate devices for account") - ); - error - })?; +) -> Result<PrivateAccountAndDevice, device::Error> { + let devices = timeout( + TIMEOUT, + service.list_devices_with_backoff(account_token.clone()), + ) + .await + .map_err(|_error| { + log::error!("Failed to enumerate devices for account: timed out"); + device::Error::Cancelled + })? + .map_err(|error| { + log::error!( + "{}", + error.display_chain_with_msg("Failed to enumerate devices for account") + ); + error + })?; for device in devices.into_iter() { if device.pubkey == wg_data.private_key.public_key() { - return Ok(DeviceData { - token, - device: InnerDevice::from(device), - wg_data, + return Ok(PrivateAccountAndDevice { + account_token, + device: PrivateDevice::try_from_device(device, wg_data)?, }); } } log::info!("The existing WireGuard key is not valid; generating a new device"); - cache_from_account(service, token).await + cache_from_account(service, account_token).await } async fn cache_from_account( service: DeviceService, - token: AccountToken, -) -> Result<DeviceData, device::Error> { - timeout(TIMEOUT, service.generate_for_account_with_backoff(token)) - .await - .map_err(|_error| { - log::error!("Failed to generate new device for account: timed out"); - device::Error::Cancelled - })? - .map_err(|error| { - log::error!( - "{}", - error.display_chain_with_msg("Failed to generate new device for account") - ); - error - }) + account_token: AccountToken, +) -> Result<PrivateAccountAndDevice, device::Error> { + timeout( + TIMEOUT, + service.generate_for_account_with_backoff(account_token), + ) + .await + .map_err(|_error| { + log::error!("Failed to generate new device for account: timed out"); + device::Error::Cancelled + })? + .map_err(|error| { + log::error!( + "{}", + error.display_chain_with_msg("Failed to generate new device for account") + ); + error + }) } diff --git a/mullvad-jni/src/classes.rs b/mullvad-jni/src/classes.rs index 3aceb3a820..a453b4ad6c 100644 --- a/mullvad-jni/src/classes.rs +++ b/mullvad-jni/src/classes.rs @@ -3,13 +3,13 @@ pub const CLASSES: &[&str] = &[ "java/net/InetAddress", "java/net/InetSocketAddress", "java/util/ArrayList", + "net/mullvad/mullvadvpn/model/AccountAndDevice", "net/mullvad/mullvadvpn/model/AccountData", "net/mullvad/mullvadvpn/model/AppVersionInfo", "net/mullvad/mullvadvpn/model/Constraint$Any", "net/mullvad/mullvadvpn/model/Constraint$Only", "net/mullvad/mullvadvpn/model/DnsOptions", "net/mullvad/mullvadvpn/model/Device", - "net/mullvad/mullvadvpn/model/DeviceConfig", "net/mullvad/mullvadvpn/model/DeviceEvent", "net/mullvad/mullvadvpn/model/RemoveDeviceEvent", "net/mullvad/mullvadvpn/model/GeoIpLocation", diff --git a/mullvad-jni/src/daemon_interface.rs b/mullvad-jni/src/daemon_interface.rs index ae774ae8ee..f429201e88 100644 --- a/mullvad-jni/src/daemon_interface.rs +++ b/mullvad-jni/src/daemon_interface.rs @@ -2,7 +2,7 @@ use futures::{channel::oneshot, executor::block_on}; use mullvad_daemon::{device, DaemonCommand, DaemonCommandSender}; use mullvad_types::{ account::{AccountData, AccountToken, VoucherSubmission}, - device::{Device, DeviceConfig}, + device::{AccountAndDevice, Device}, location::GeoIpLocation, relay_constraints::RelaySettingsUpdate, relay_list::RelayList, @@ -212,7 +212,7 @@ impl DaemonInterface { .map_err(Error::from) } - pub fn get_device(&self) -> Result<Option<DeviceConfig>> { + pub fn get_device(&self) -> Result<Option<AccountAndDevice>> { let (tx, rx) = oneshot::channel(); self.send_command(DaemonCommand::GetDevice(tx))?; diff --git a/mullvad-management-interface/src/types.rs b/mullvad-management-interface/src/types.rs index 13721066a5..d685899256 100644 --- a/mullvad-management-interface/src/types.rs +++ b/mullvad-management-interface/src/types.rs @@ -231,7 +231,7 @@ impl From<mullvad_types::device::DeviceEvent> for DeviceEvent { fn from(event: mullvad_types::device::DeviceEvent) -> Self { DeviceEvent { device: event.device.map(|config| DeviceConfig { - account_token: config.token, + account_token: config.account_token, device: Some(Device::from(config.device)), }), remote: event.remote, @@ -249,10 +249,10 @@ impl From<mullvad_types::device::RemoveDeviceEvent> for RemoveDeviceEvent { } } -impl From<mullvad_types::device::DeviceConfig> for DeviceConfig { - fn from(device: mullvad_types::device::DeviceConfig) -> Self { +impl From<mullvad_types::device::AccountAndDevice> for DeviceConfig { + fn from(device: mullvad_types::device::AccountAndDevice) -> Self { DeviceConfig { - account_token: device.token, + account_token: device.account_token, device: Some(Device::from(device.device)), } } diff --git a/mullvad-setup/src/main.rs b/mullvad-setup/src/main.rs index 10ebb36981..15a0c7dd6d 100644 --- a/mullvad-setup/src/main.rs +++ b/mullvad-setup/src/main.rs @@ -183,7 +183,7 @@ async fn remove_device() -> Result<(), Error> { .await, ); retry_future_n( - move || proxy.remove(device.token.clone(), device.device.id.clone()), + move || proxy.remove(device.account_token.clone(), device.device.id.clone()), move |result| match result { Err(error) => error.is_network_error(), _ => false, diff --git a/mullvad-types/src/device.rs b/mullvad-types/src/device.rs index 0a0dd4d91d..93836c674a 100644 --- a/mullvad-types/src/device.rs +++ b/mullvad-types/src/device.rs @@ -1,4 +1,4 @@ -use crate::{account::AccountToken, wireguard}; +use crate::account::AccountToken; #[cfg(target_os = "android")] use jnix::IntoJava; use serde::{Deserialize, Serialize}; @@ -61,57 +61,20 @@ impl fmt::Display for DevicePort { } } -/// A complete device configuration. -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -pub struct DeviceData { - pub token: AccountToken, - pub device: InnerDevice, - pub wg_data: wireguard::WireguardData, -} - -impl From<DeviceData> for Device { - fn from(data: DeviceData) -> Device { - Device { - id: data.device.id, - name: data.device.name, - pubkey: data.wg_data.private_key.public_key(), - ports: data.device.ports, - } - } -} - -/// Device data used by [DeviceData]. -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -pub struct InnerDevice { - pub id: DeviceId, - pub name: DeviceName, - pub ports: Vec<DevicePort>, -} - -impl From<Device> for InnerDevice { - fn from(device: Device) -> Self { - InnerDevice { - id: device.id, - name: device.name, - ports: device.ports, - } - } -} - -/// [`DeviceData`] excluding the private key. +/// A [Device] and its associated account token. #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] #[cfg_attr(target_os = "android", derive(IntoJava))] #[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))] -pub struct DeviceConfig { - pub token: AccountToken, +pub struct AccountAndDevice { + pub account_token: AccountToken, pub device: Device, } -impl From<DeviceData> for DeviceConfig { - fn from(data: DeviceData) -> DeviceConfig { - DeviceConfig { - token: data.token.clone(), - device: Device::from(data), +impl AccountAndDevice { + pub fn new(account_token: AccountToken, device: Device) -> Self { + Self { + account_token, + device, } } } @@ -122,22 +85,19 @@ impl From<DeviceData> for DeviceConfig { #[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))] pub struct DeviceEvent { /// Device that was affected. - pub device: Option<DeviceConfig>, + pub device: Option<AccountAndDevice>, /// Indicates whether the change was initiated remotely or by the daemon. pub remote: bool, } impl DeviceEvent { - pub fn new(data: Option<DeviceData>, remote: bool) -> DeviceEvent { - DeviceEvent { - device: data.map(DeviceConfig::from), - remote, - } + pub fn new(device: Option<AccountAndDevice>, remote: bool) -> DeviceEvent { + DeviceEvent { device, remote } } - pub fn from_device(data: DeviceData, remote: bool) -> DeviceEvent { + pub fn from_device(device: AccountAndDevice, remote: bool) -> DeviceEvent { DeviceEvent { - device: Some(DeviceConfig::from(data)), + device: Some(device), remote, } } |
