[Enterprise Signals] Get Mac Firewall Setting from socketfilterfw on MacOS 15+ only

The previous method is no longer supported on the upcoming new Sequoia OS, but we still need them to keep supporting older versions of MacOS.

This is a reland of crrev.com/c/5839553 and crrev.com/c/5845392, but limited the change to be MacOS v15+ only.

Fixed: 362191723

Change-Id: Ifc7e3f86f04bf2ae3da17c9460d95cbf42740e34
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5916765
Reviewed-by: Dominique Fauteux-Chapleau <domfc@chromium.org>
Commit-Queue: Zonghan Xu <xzonghan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1365773}
diff --git a/chrome/browser/enterprise/signals/context_info_fetcher.cc b/chrome/browser/enterprise/signals/context_info_fetcher.cc
index dc58a6ad..0f0d89fd 100644
--- a/chrome/browser/enterprise/signals/context_info_fetcher.cc
+++ b/chrome/browser/enterprise/signals/context_info_fetcher.cc
@@ -42,6 +42,9 @@
 
 #if BUILDFLAG(IS_MAC)
 #include <CoreFoundation/CoreFoundation.h>
+
+#include "base/mac/mac_util.h"
+#include "base/process/launch.h"
 #endif
 
 #if BUILDFLAG(IS_WIN)
@@ -137,34 +140,63 @@
 
 #if BUILDFLAG(IS_MAC)
 SettingValue GetMacOSFirewall() {
-  // There is no official Apple documentation on how to obtain the enabled
-  // status of the firewall (System Preferences> Security & Privacy> Firewall).
-  // Reading globalstate from com.apple.alf is the closest way to get such an
-  // API in Chrome without delegating to potentially unstable commands.
-  // Values of "globalstate":
-  //   0 = de-activated
-  //   1 = on for specific services
-  //   2 = on for essential services
-  // You can get 2 by, e.g., enabling the "Block all incoming connections"
-  // firewall functionality.
+  if (base::mac::MacOSMajorVersion() < 15) {
+    // There is no official Apple documentation on how to obtain the enabled
+    // status of the firewall (System Preferences> Security & Privacy> Firewall)
+    // prior to MacOS versions 15. Reading globalstate from com.apple.alf is the
+    // closest way to get such an API in Chrome without delegating to
+    // potentially unstable commands. Values of "globalstate":
+    //   0 = de-activated
+    //   1 = on for specific services
+    //   2 = on for essential services
+    // You can get 2 by, e.g., enabling the "Block all incoming connections"
+    // firewall functionality.
+    Boolean key_exists_with_valid_format = false;
+    CFIndex globalstate = CFPreferencesGetAppIntegerValue(
+        CFSTR("globalstate"), CFSTR("com.apple.alf"),
+        &key_exists_with_valid_format);
 
-  Boolean key_exists_with_valid_format = false;
-  CFIndex globalstate = CFPreferencesGetAppIntegerValue(
-      CFSTR("globalstate"), CFSTR("com.apple.alf"),
-      &key_exists_with_valid_format);
-
-  if (!key_exists_with_valid_format)
-    return SettingValue::UNKNOWN;
-
-  switch (globalstate) {
-    case 0:
-      return SettingValue::DISABLED;
-    case 1:
-    case 2:
-      return SettingValue::ENABLED;
-    default:
+    if (!key_exists_with_valid_format) {
       return SettingValue::UNKNOWN;
+    }
+
+    switch (globalstate) {
+      case 0:
+        return SettingValue::DISABLED;
+      case 1:
+      case 2:
+        return SettingValue::ENABLED;
+      default:
+        return SettingValue::UNKNOWN;
+    }
   }
+
+  // Based on this recommendation from Apple:
+  // https://developer.apple.com/documentation/macos-release-notes/macos-15-release-notes/#Application-Firewall
+  base::FilePath fw_util("/usr/libexec/ApplicationFirewall/socketfilterfw");
+  if (!base::PathExists(fw_util)) {
+    return SettingValue::UNKNOWN;
+  }
+
+  base::CommandLine command(fw_util);
+  command.AppendSwitch("getglobalstate");
+  std::string output;
+  if (!base::GetAppOutput(command, &output)) {
+    return SettingValue::UNKNOWN;
+  }
+
+  // State 1 is when the Firewall is simply enabled.
+  // State 2 is when the Firewall is enabled and all incoming connections are
+  // blocked.
+  if (output.find("(State = 1)") != std::string::npos ||
+      output.find("(State = 2)") != std::string::npos) {
+    return SettingValue::ENABLED;
+  }
+  if (output.find("(State = 0)") != std::string::npos) {
+    return SettingValue::DISABLED;
+  }
+
+  return SettingValue::UNKNOWN;
 }
 #endif