diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2023-12-22 11:50:55 +0100 |
|---|---|---|
| committer | Markus Pettersson <markus.pettersson@mullvad.net> | 2024-01-03 09:43:12 +0100 |
| commit | 2fda59b1164e3d2a627cd3fd5960c2d7e0abb848 (patch) | |
| tree | 19abe0cb48d8b9b9a898230b8798fe8e3d0d1e13 | |
| parent | 4e424284c511e646fb81d256b2ba8fa4dc3d08f9 (diff) | |
| download | mullvadvpn-2fda59b1164e3d2a627cd3fd5960c2d7e0abb848.tar.xz mullvadvpn-2fda59b1164e3d2a627cd3fd5960c2d7e0abb848.zip | |
Prevent the last access method from being disabled
Add proper checks around disabling the last enabled access method. If
the daemon detects that any one access method update would cause the
last enabled access method to become disabled, it will find & toggle the
`Direct` access method setting to be enabled.
| -rw-r--r-- | mullvad-daemon/src/access_method.rs | 35 | ||||
| -rw-r--r-- | mullvad-types/src/access_method.rs | 39 |
2 files changed, 54 insertions, 20 deletions
diff --git a/mullvad-daemon/src/access_method.rs b/mullvad-daemon/src/access_method.rs index 028e54f462..3bea2c4c7d 100644 --- a/mullvad-daemon/src/access_method.rs +++ b/mullvad-daemon/src/access_method.rs @@ -81,7 +81,7 @@ where ) -> Result<(), Error> { // Make sure that we are not trying to remove a built-in API access // method - let command = match self.settings.api_access_methods.find(&access_method) { + let command = match self.settings.api_access_methods.find_by_id(&access_method) { Some(api_access_method) => { if api_access_method.is_builtin() { Err(Error::RemoveBuiltIn) @@ -131,7 +131,7 @@ where ) -> Result<AccessMethodSetting, Error> { self.settings .api_access_methods - .find(&access_method) + .find_by_id(&access_method) .ok_or(Error::NoSuchMethod(access_method)) .cloned() } @@ -147,22 +147,37 @@ where &mut self, access_method_update: AccessMethodSetting, ) -> Result<(), Error> { - // We have to be a bit careful. If we are about to disable the last - // remaining enabled access method, we would cause an inconsistent state - // in the daemon's settings. Therefore, we have to safeguard against - // this by explicitly checking for & disallow any update which would - // cause the last enabled access method to become disabled. + // If the currently active access method is updated, we need to re-set + // it after updating the settings. let current = self.get_current_access_method().await?; let mut command = Command::Nothing; let settings_update = |settings: &mut Settings| { - if let Some(access_method) = settings - .api_access_methods - .find_mut(&access_method_update.get_id()) + let access_methods = &mut settings.api_access_methods; + if let Some(access_method) = + access_methods.find_by_id_mut(&access_method_update.get_id()) { *access_method = access_method_update; if access_method.get_id() == current.get_id() { command = Command::Set(access_method.get_id()) } + // We have to be a bit careful. If we are about to disable the last + // remaining enabled access method, we would cause an inconsistent state + // in the daemon's settings. Therefore, we have to safeguard against + // this by explicitly checking for any update which would cause the last + // enabled access method to become disabled. In that case, we should + // re-enable the `Direct` access method. + if access_methods.collect_enabled().is_empty() { + if let Some(direct) = access_methods.get_direct() { + direct.enabled = true; + } else { + // If the `Direct` access method does not exist within the + // settings for some reason, the settings are in an + // inconsistent state. We don't have much choice but to + // reset these settings to their default value. + log::warn!("The built-in access methods can not be found. This might be due to a corrupt settings file"); + *access_methods = access_method::Settings::default(); + } + } } }; diff --git a/mullvad-types/src/access_method.rs b/mullvad-types/src/access_method.rs index 7afaf94dfc..0c697acbbf 100644 --- a/mullvad-types/src/access_method.rs +++ b/mullvad-types/src/access_method.rs @@ -21,22 +21,34 @@ impl Settings { self.retain(|method| method.get_id() != *api_access_method) } - /// Search for a particular [`AccessMethod`] in `api_access_methods`. - pub fn find(&self, element: &Id) -> Option<&AccessMethodSetting> { + /// Search for any [`AccessMethod`] in `api_access_methods` which matches `predicate`. + pub fn find<P>(&self, predicate: P) -> Option<&AccessMethodSetting> + where + P: Fn(&AccessMethodSetting) -> bool, + { self.access_method_settings .iter() - .find(|api_access_method| *element == api_access_method.get_id()) + .find(|api_access_method| predicate(api_access_method)) } - /// Search for a particular [`AccessMethod`] in `api_access_methods`. - /// - /// If the [`AccessMethod`] is found to be part of `api_access_methods`, a - /// mutable reference to that inner element is returned. Otherwise, `None` - /// is returned. - pub fn find_mut(&mut self, element: &Id) -> Option<&mut AccessMethodSetting> { + /// Search for any [`AccessMethod`] in `api_access_methods`. + pub fn find_mut<P>(&mut self, predicate: P) -> Option<&mut AccessMethodSetting> + where + P: Fn(&AccessMethodSetting) -> bool, + { self.access_method_settings .iter_mut() - .find(|api_access_method| *element == api_access_method.get_id()) + .find(|api_access_method| predicate(api_access_method)) + } + + /// Search for a particular [`AccessMethod`] in `api_access_methods`. + pub fn find_by_id(&self, element: &Id) -> Option<&AccessMethodSetting> { + self.find(|api_access_method| *element == api_access_method.get_id()) + } + + /// Search for a particular [`AccessMethod`] in `api_access_methods`. + pub fn find_by_id_mut(&mut self, element: &Id) -> Option<&mut AccessMethodSetting> { + self.find_mut(|api_access_method| *element == api_access_method.get_id()) } /// Equivalent to [`Vec::retain`]. @@ -52,6 +64,13 @@ impl Settings { self.access_method_settings.clone() } + /// Get a reference to the `Direct` access method instance of this [`Settings`]. + pub fn get_direct(&mut self) -> Option<&mut AccessMethodSetting> { + self.find_mut(|access_method| { + access_method.access_method == BuiltInAccessMethod::Direct.into() + }) + } + pub fn direct() -> AccessMethodSetting { let method = BuiltInAccessMethod::Direct; AccessMethodSetting::new(method.canonical_name(), true, AccessMethod::from(method)) |
