summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2025-08-28 14:37:03 +0200
committerMarkus Pettersson <markus.pettersson@mullvad.net>2025-08-28 14:37:03 +0200
commit7390b0245f5b99736fdc8f273500d57c9dc0dce1 (patch)
tree320dd43be7f2681a0b70a1fe71ff06fb89b8e29c
parent8250730d9ee6308eb7e88134fc9497a5f22cebed (diff)
parent8b7e2dba790e90a0ac885adfeded15eb97fb99f0 (diff)
downloadmullvadvpn-7390b0245f5b99736fdc8f273500d57c9dc0dce1.tar.xz
mullvadvpn-7390b0245f5b99736fdc8f273500d57c9dc0dce1.zip
Merge branch 'investigate-error-while-checking-launch-daemon-authorization-des-2147'
-rw-r--r--Cargo.lock124
-rw-r--r--mullvad-daemon/Cargo.toml3
-rw-r--r--mullvad-daemon/src/macos_launch_daemon.rs110
3 files changed, 133 insertions, 104 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 967f5e4081..dded76c819 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -436,7 +436,16 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
dependencies = [
- "objc2",
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "block2"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2"
+dependencies = [
+ "objc2 0.6.2",
]
[[package]]
@@ -1089,6 +1098,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
+name = "dispatch2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
+dependencies = [
+ "bitflags 2.9.0",
+ "objc2 0.6.2",
+]
+
+[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2967,7 +2986,8 @@ dependencies = [
"mullvad-version",
"nix 0.30.1",
"notify 8.0.0",
- "objc2",
+ "objc2-foundation 0.3.1",
+ "objc2-service-management",
"rand 0.8.5",
"regex",
"serde",
@@ -3545,7 +3565,7 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
name = "nseventforwarder"
version = "0.0.0"
dependencies = [
- "block2",
+ "block2 0.5.1",
"neon",
"objc2-app-kit",
]
@@ -3590,9 +3610,6 @@ name = "objc-sys"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
-dependencies = [
- "cc",
-]
[[package]]
name = "objc2"
@@ -3605,18 +3622,27 @@ dependencies = [
]
[[package]]
+name = "objc2"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc"
+dependencies = [
+ "objc2-encode",
+]
+
+[[package]]
name = "objc2-app-kit"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
dependencies = [
"bitflags 2.9.0",
- "block2",
+ "block2 0.5.1",
"libc",
- "objc2",
+ "objc2 0.5.2",
"objc2-core-data",
"objc2-core-image",
- "objc2-foundation",
+ "objc2-foundation 0.2.2",
"objc2-quartz-core",
]
@@ -3627,9 +3653,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
dependencies = [
"bitflags 2.9.0",
- "block2",
- "objc2",
- "objc2-foundation",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
+dependencies = [
+ "bitflags 2.9.0",
+ "dispatch2",
+ "objc2 0.6.2",
]
[[package]]
@@ -3638,17 +3675,17 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
dependencies = [
- "block2",
- "objc2",
- "objc2-foundation",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
"objc2-metal",
]
[[package]]
name = "objc2-encode"
-version = "4.0.3"
+version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]]
name = "objc2-foundation"
@@ -3657,9 +3694,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
"bitflags 2.9.0",
- "block2",
+ "block2 0.5.1",
+ "libc",
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
+dependencies = [
+ "bitflags 2.9.0",
+ "block2 0.6.1",
"libc",
- "objc2",
+ "objc2 0.6.2",
+ "objc2-core-foundation",
]
[[package]]
@@ -3669,9 +3719,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
dependencies = [
"bitflags 2.9.0",
- "block2",
- "objc2",
- "objc2-foundation",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
]
[[package]]
@@ -3681,13 +3731,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
dependencies = [
"bitflags 2.9.0",
- "block2",
- "objc2",
- "objc2-foundation",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
"objc2-metal",
]
[[package]]
+name = "objc2-security"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44"
+dependencies = [
+ "bitflags 2.9.0",
+ "objc2 0.6.2",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-service-management"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62ca650c581044d08572b3c21d9ce8c730f2eefdc1683f033849f305ea8212be"
+dependencies = [
+ "block2 0.6.1",
+ "objc2 0.6.2",
+ "objc2-core-foundation",
+ "objc2-foundation 0.3.1",
+ "objc2-security",
+]
+
+[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index 7921bf9c2c..7abf9bf7cc 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -71,7 +71,8 @@ simple-signal = "1.1"
talpid-dbus = { path = "../talpid-dbus" }
[target.'cfg(target_os="macos")'.dependencies]
-objc2 = { version = "0.5.2", features = ["exception"] }
+objc2-foundation = { version = "0.3.1", features = ["NSProcessInfo", "NSString", "NSURL"] }
+objc2-service-management = { version = "0.3.1", features = [ "SMAppService", "objc2" ] }
notify = "8.0.0"
talpid-macos = { path = "../talpid-macos" }
diff --git a/mullvad-daemon/src/macos_launch_daemon.rs b/mullvad-daemon/src/macos_launch_daemon.rs
index 94cb5b88e1..080e771565 100644
--- a/mullvad-daemon/src/macos_launch_daemon.rs
+++ b/mullvad-daemon/src/macos_launch_daemon.rs
@@ -5,40 +5,13 @@
//! must be checked so that the user can be directed to approve the launch
//! daemon in the system settings.
-#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+use objc2_foundation::{NSOperatingSystemVersion, NSProcessInfo, NSURL, ns_string};
+use objc2_service_management::{SMAppService, SMAppServiceStatus};
-use libc::c_longlong;
-use objc2::{Encode, Encoding, RefEncode, class, msg_send, runtime::AnyObject};
-use std::ffi::CStr;
-
-type Id = *mut AnyObject;
-
-// Framework that contains `SMAppService`.
-#[link(name = "ServiceManagement", kind = "framework")]
-unsafe extern "C" {}
-
-/// Returned by `[NSProcessInfo operatingSystemVersion]`. Contains the current
-#[repr(C)]
-#[derive(Debug)]
-struct NSOperatingSystemVersion {
- major_version: c_longlong,
- minor_version: c_longlong,
- patch_version: c_longlong,
-}
-
-/// Implement Objective-C type encoding for the struct. Allows the `objc2` crate
-/// to perform function signature matching before performing calls into the Objective-C
-/// runtime.
-unsafe impl Encode for NSOperatingSystemVersion {
- const ENCODING: Encoding = Encoding::Struct(
- "NSOperatingSystemVersion",
- &[Encoding::LongLong, Encoding::LongLong, Encoding::LongLong],
- );
-}
-
-unsafe impl RefEncode for NSOperatingSystemVersion {
- const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
-}
+/// Path to the plist that defines the Mullvad launch daemon.
+/// It must be kept in sync with the path defined in
+/// `dist-assets/pkg-scripts/postinstall`.
+const DAEMON_PLIST_PATH: &str = "/Library/LaunchDaemons/net.mullvad.daemon.plist";
/// Authorization status of the Mullvad daemon.
#[repr(i32)]
@@ -53,60 +26,41 @@ pub enum LaunchDaemonStatus {
/// NOTE: On macos < 13, this function always returns `LaunchDaemonStatus::Ok`.
pub fn get_status() -> LaunchDaemonStatus {
// `SMAppService` does not exist if the major version is less than 13.
- if get_os_version().major_version < 13 {
+ let os_version = get_os_version();
+ if os_version.majorVersion < 13 {
return LaunchDaemonStatus::Ok;
}
- get_status_for_url(&daemon_plist_url())
+ // SAFETY: daemon_plist_path is not an empty path & it is a valid system path.
+ // https://developer.apple.com/documentation/foundation/nsurl/fileurl(withpath:)#parameters
+ let daemon_plist_url = unsafe { NSURL::fileURLWithPath(ns_string!(DAEMON_PLIST_PATH)) };
+ get_status_for_url(&daemon_plist_url)
}
-fn get_status_for_url(url: &Object) -> LaunchDaemonStatus {
- let status: libc::c_long =
- unsafe { msg_send![class!(SMAppService), statusForLegacyURL: url.0] };
-
+fn get_status_for_url(url: &NSURL) -> LaunchDaemonStatus {
+ // SAFETY: Apple does not state *anything* regarding safety requirements of this function:
+ // https://developer.apple.com/documentation/servicemanagement/smappservice/statusforlegacyplist(at:)
+ // But using a bit of reasoning & the [guidelines of objc2](https://github.com/madsmtm/objc2/blob/master/crates/header-translator/README.md#what-is-required-for-a-method-to-be-safe):
+ // """
+ // What is required for a method to be safe?
+ // 1. The method must not take a raw pointer; one could trivially pass ptr::invalid() and cause UB with that.
+ // 2. Any extra requirements that the method states in its documentation must be upheld.
+ // """
+ // we can conclude that:
+ // (1.) is upheld by the virtue of url being a reference, since references are always valid.
+ // (2.) is trivially upheld since Apple does not state safety requirements.
+ let status = unsafe { SMAppService::statusForLegacyURL(url) };
match status {
- // SMAppServiceStatusNotRegistered | SMAppServiceStatusNotFound
- 0 | 3 => LaunchDaemonStatus::NotFound,
- // SMAppServiceStatusEnabled
- 1 => LaunchDaemonStatus::Ok,
- // SMAppServiceStatusRequiresApproval
- 2 => LaunchDaemonStatus::NotAuthorized,
+ SMAppServiceStatus::NotRegistered | SMAppServiceStatus::NotFound => {
+ LaunchDaemonStatus::NotFound
+ }
+ SMAppServiceStatus::Enabled => LaunchDaemonStatus::Ok,
+ SMAppServiceStatus::RequiresApproval => LaunchDaemonStatus::NotAuthorized,
// Unknown status
_ => LaunchDaemonStatus::Unknown,
}
}
fn get_os_version() -> NSOperatingSystemVersion {
- // the object is lazily instantiated, so we don't release it
- let proc_info: Id = unsafe { msg_send![class!(NSProcessInfo), processInfo] };
- unsafe { msg_send![proc_info, operatingSystemVersion] }
-}
-
-/// Returns an `NSURL` instance for `DAEMON_PLIST_PATH`.
-fn daemon_plist_url() -> Object {
- /// Path to the plist that defines the Mullvad launch daemon.
- /// It must be kept in sync with the path defined in
- /// `dist-assets/pkg-scripts/postinstall`.
- const DAEMON_PLIST_PATH: &CStr = c"/Library/LaunchDaemons/net.mullvad.daemon.plist";
-
- let nsstr_inst: Id = unsafe { msg_send![class!(NSString), alloc] };
- let nsstr_inst: Id =
- unsafe { msg_send![nsstr_inst, initWithUTF8String: DAEMON_PLIST_PATH.as_ptr()] };
-
- let nsurl_inst: Id = unsafe { msg_send![class!(NSURL), alloc] };
- let nsurl_inst: Id = unsafe { msg_send![nsurl_inst, initWithString: nsstr_inst] };
-
- let _: () = unsafe { msg_send![nsstr_inst, release] };
-
- assert!(!nsurl_inst.is_null());
-
- Object(nsurl_inst)
-}
-
-/// Calls `[self.0 release]` when the wrapped instance is dropped.
-struct Object(Id);
-
-impl Drop for Object {
- fn drop(&mut self) {
- let _: () = unsafe { msg_send![self.0, release] };
- }
+ let process_info = NSProcessInfo::processInfo();
+ process_info.operatingSystemVersion()
}