summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2025-01-22 13:56:13 +0100
committerMarkus Pettersson <markus.pettersson@mullvad.net>2025-01-22 13:56:13 +0100
commit0072998b499d9a7161db44684f8a3d5b470a18c8 (patch)
tree7e32173e989a6ad0476b9845ddbc4c9c5fae3220
parentbbfc9c858bd58cf8a8768e7229b40ee44aa898bd (diff)
parentd4f9ee5c71cb5ced3537fe23ba257ae2d2db60b4 (diff)
downloadmullvadvpn-0072998b499d9a7161db44684f8a3d5b470a18c8.tar.xz
mullvadvpn-0072998b499d9a7161db44684f8a3d5b470a18c8.zip
Merge branch 'connection-still-in-blocked-state-after-disabling-split-des-1567'
-rw-r--r--CHANGELOG.md4
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx5
-rw-r--r--mullvad-daemon/src/lib.rs43
-rw-r--r--mullvad-daemon/src/settings/mod.rs4
4 files changed, 44 insertions, 12 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 71e63f6084..b47012e172 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,6 +36,10 @@ Line wrap the file at 100 chars. Th
### Fixed
- (macOS and Windows only) Add the correct route when using obfuscation with Wireguard.
+#### macOS
+- Fix daemon ending up in blocked state if the user toggled split tunneling without having granted
+ Full Disk Access to `mullvad-daemon`. This could only ever be accomplished from the CLI.
+
## [2025.2] - 2025-01-08
### Fixed
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx
index df3c78d4b0..20b001f095 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/NotificationArea.tsx
@@ -46,7 +46,7 @@ interface IProps {
}
export default function NotificationArea(props: IProps) {
- const { showFullDiskAccessSettings, reconnectTunnel } = useAppContext();
+ const { showFullDiskAccessSettings } = useAppContext();
const account = useSelector((state: IReduxState) => state.account);
const locale = useSelector((state: IReduxState) => state.userInterface.locale);
@@ -80,8 +80,7 @@ export default function NotificationArea(props: IProps) {
const disableSplitTunneling = useCallback(async () => {
setIsModalOpen(false);
await setSplitTunnelingState(false);
- await reconnectTunnel();
- }, [reconnectTunnel, setSplitTunnelingState]);
+ }, [setSplitTunnelingState]);
const notificationProviders: InAppNotificationProvider[] = [
new ConnectingNotificationProvider({ tunnelState }),
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index b313e274bc..aa6a21a564 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -1021,7 +1021,7 @@ impl Daemon {
TunnelStateTransition::Disconnected => TunnelState::Disconnected { location: None },
TunnelStateTransition::Connecting(endpoint) => {
let feature_indicators = compute_feature_indicators(
- &self.settings.to_settings(),
+ self.settings.settings(),
&endpoint,
self.parameters_generator.last_relay_was_overridden().await,
);
@@ -1033,7 +1033,7 @@ impl Daemon {
}
TunnelStateTransition::Connected(endpoint) => {
let feature_indicators = compute_feature_indicators(
- &self.settings.to_settings(),
+ self.settings.settings(),
&endpoint,
self.parameters_generator.last_relay_was_overridden().await,
);
@@ -1199,7 +1199,7 @@ impl Daemon {
.active_features()
.any(|f| matches!(&f, FeatureIndicator::ServerIpOverride));
let new_feature_indicators =
- compute_feature_indicators(&self.settings.to_settings(), endpoint, ip_override);
+ compute_feature_indicators(self.settings.settings(), endpoint, ip_override);
// Update and broadcast the new feature indicators if they have changed
if *feature_indicators != new_feature_indicators {
// Make sure to update the daemon's actual tunnel state. Otherwise, feature
@@ -1539,11 +1539,36 @@ impl Daemon {
tx: ResponseTx<(), Error>,
) {
let save_result = match update {
- ExcludedPathsUpdate::SetState(state) => self
- .settings
- .update(move |settings| settings.split_tunnel.enable_exclusions = state)
- .await
- .map_err(Error::SettingsError),
+ ExcludedPathsUpdate::SetState(state) => {
+ let split_tunnel_was_enabled =
+ self.settings.settings().split_tunnel.enable_exclusions;
+ let save_result = self
+ .settings
+ .update(move |settings| settings.split_tunnel.enable_exclusions = state)
+ .await
+ .map_err(Error::SettingsError);
+ // If the user enables split tunneling without also enabling Full Disk Access
+ // (FDA), the daemon will enter the error state. This is unlikely, since it should
+ // only be possible via the CLI or if the user manages to disable FDA after having
+ // successfully enabled split tunneling. In any case, We have observed users
+ // getting confused over being blocked in this case, and this we may want to
+ // reconnect after disabling split tunneling.
+ //
+ // Since FDA is an implementation detail of split tunneling, we don't actually have
+ // a way of getting this information at this point, so we fallback to issuing a
+ // reconnect if the user disables split tunneling while in the error state. This
+ // code can be removed if we ever remove our dependency on FDA.
+ if cfg!(target_os = "macos") {
+ let split_tunnel_will_be_disabled = !state;
+ if self.tunnel_state.is_in_error_state()
+ && split_tunnel_was_enabled
+ && split_tunnel_will_be_disabled
+ {
+ self.reconnect_tunnel();
+ }
+ }
+ save_result
+ }
ExcludedPathsUpdate::SetPaths(paths) => self
.settings
.update(move |settings| settings.split_tunnel.apps = paths)
@@ -2508,7 +2533,7 @@ impl Daemon {
{
Ok(settings_changed) => {
if settings_changed {
- let settings = self.settings.to_settings();
+ let settings = self.settings.settings();
let resolvers =
dns::addresses_from_options(&settings.tunnel_options.dns_options);
self.send_tunnel_command(TunnelCommand::Dns(
diff --git a/mullvad-daemon/src/settings/mod.rs b/mullvad-daemon/src/settings/mod.rs
index 33513d90da..dcb7e56106 100644
--- a/mullvad-daemon/src/settings/mod.rs
+++ b/mullvad-daemon/src/settings/mod.rs
@@ -248,6 +248,10 @@ impl SettingsPersister {
Ok(())
}
+ pub const fn settings(&self) -> &Settings {
+ &self.settings
+ }
+
pub fn to_settings(&self) -> Settings {
self.settings.clone()
}