diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2025-08-28 14:37:03 +0200 |
|---|---|---|
| committer | Markus Pettersson <markus.pettersson@mullvad.net> | 2025-08-28 14:37:03 +0200 |
| commit | 7390b0245f5b99736fdc8f273500d57c9dc0dce1 (patch) | |
| tree | 320dd43be7f2681a0b70a1fe71ff06fb89b8e29c | |
| parent | 8250730d9ee6308eb7e88134fc9497a5f22cebed (diff) | |
| parent | 8b7e2dba790e90a0ac885adfeded15eb97fb99f0 (diff) | |
| download | mullvadvpn-7390b0245f5b99736fdc8f273500d57c9dc0dce1.tar.xz mullvadvpn-7390b0245f5b99736fdc8f273500d57c9dc0dce1.zip | |
Merge branch 'investigate-error-while-checking-launch-daemon-authorization-des-2147'
| -rw-r--r-- | Cargo.lock | 124 | ||||
| -rw-r--r-- | mullvad-daemon/Cargo.toml | 3 | ||||
| -rw-r--r-- | mullvad-daemon/src/macos_launch_daemon.rs | 110 |
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() } |
