123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/enterprise_util.h"
- #import <OpenDirectory/OpenDirectory.h>
- #include <string>
- #include <vector>
- #include "base/logging.h"
- #include "base/mac/foundation_util.h"
- #include "base/process/launch.h"
- #include "base/strings/string_split.h"
- #include "base/strings/string_util.h"
- #include "base/strings/sys_string_conversions.h"
- namespace base {
- bool IsManagedDevice() {
- // MDM enrollment indicates the device is actively being managed. Simply being
- // joined to a domain, however, does not.
- // IsDeviceRegisteredWithManagementNew is only available after 10.13.4.
- // Eventually switch to it when that is the minimum OS required by Chromium.
- if (@available(macOS 10.13.4, *)) {
- base::MacDeviceManagementStateNew mdm_state =
- base::IsDeviceRegisteredWithManagementNew();
- return mdm_state ==
- base::MacDeviceManagementStateNew::kLimitedMDMEnrollment ||
- mdm_state == base::MacDeviceManagementStateNew::kFullMDMEnrollment ||
- mdm_state == base::MacDeviceManagementStateNew::kDEPMDMEnrollment;
- }
- base::MacDeviceManagementStateOld mdm_state =
- base::IsDeviceRegisteredWithManagementOld();
- return mdm_state == base::MacDeviceManagementStateOld::kMDMEnrollment;
- }
- bool IsEnterpriseDevice() {
- // Domain join is a basic indicator of being an enterprise device.
- DeviceUserDomainJoinState join_state = AreDeviceAndUserJoinedToDomain();
- return join_state.device_joined || join_state.user_joined;
- }
- MacDeviceManagementStateOld IsDeviceRegisteredWithManagementOld() {
- static MacDeviceManagementStateOld state = [] {
- @autoreleasepool {
- std::vector<std::string> profiler_argv{"/usr/sbin/system_profiler",
- "SPConfigurationProfileDataType",
- "-detailLevel",
- "mini",
- "-timeout",
- "15",
- "-xml"};
- std::string profiler_stdout;
- if (!GetAppOutput(profiler_argv, &profiler_stdout)) {
- LOG(WARNING) << "Could not get system_profiler output.";
- return MacDeviceManagementStateOld::kFailureAPIUnavailable;
- };
- NSArray* root = base::mac::ObjCCast<NSArray>([NSPropertyListSerialization
- propertyListWithData:[SysUTF8ToNSString(profiler_stdout)
- dataUsingEncoding:NSUTF8StringEncoding]
- options:NSPropertyListImmutable
- format:nil
- error:nil]);
- if (!root) {
- LOG(WARNING) << "Could not parse system_profiler output.";
- return MacDeviceManagementStateOld::kFailureUnableToParseResult;
- };
- for (NSDictionary* results in root) {
- for (NSDictionary* dict in results[@"_items"]) {
- for (NSDictionary* device_config_profiles in dict[@"_items"]) {
- for (NSDictionary* profile_item in
- device_config_profiles[@"_items"]) {
- if (![profile_item[@"_name"] isEqual:@"com.apple.mdm"])
- continue;
- NSString* payload_data =
- profile_item[@"spconfigprofile_payload_data"];
- NSDictionary* payload_data_dict =
- base::mac::ObjCCast<NSDictionary>([NSPropertyListSerialization
- propertyListWithData:
- [payload_data dataUsingEncoding:NSUTF8StringEncoding]
- options:NSPropertyListImmutable
- format:nil
- error:nil]);
- if (!payload_data_dict)
- continue;
- // Verify that the URL validates.
- if ([NSURL URLWithString:payload_data_dict[@"CheckInURL"]])
- return MacDeviceManagementStateOld::kMDMEnrollment;
- }
- }
- }
- }
- return MacDeviceManagementStateOld::kNoEnrollment;
- }
- }();
- return state;
- }
- MacDeviceManagementStateNew IsDeviceRegisteredWithManagementNew() {
- static MacDeviceManagementStateNew state = [] {
- if (@available(macOS 10.13.4, *)) {
- std::vector<std::string> profiles_argv{"/usr/bin/profiles", "status",
- "-type", "enrollment"};
- std::string profiles_stdout;
- if (!GetAppOutput(profiles_argv, &profiles_stdout)) {
- LOG(WARNING) << "Could not get profiles output.";
- return MacDeviceManagementStateNew::kFailureAPIUnavailable;
- }
- // Sample output of `profiles` with full MDM enrollment:
- // Enrolled via DEP: Yes
- // MDM enrollment: Yes (User Approved)
- // MDM server: https://applemdm.example.com/some/path?foo=bar
- StringPairs property_states;
- if (!SplitStringIntoKeyValuePairs(profiles_stdout, ':', '\n',
- &property_states)) {
- return MacDeviceManagementStateNew::kFailureUnableToParseResult;
- }
- bool enrolled_via_dep = false;
- bool mdm_enrollment_not_approved = false;
- bool mdm_enrollment_user_approved = false;
- for (const auto& property_state : property_states) {
- StringPiece property =
- TrimString(property_state.first, kWhitespaceASCII, TRIM_ALL);
- StringPiece state =
- TrimString(property_state.second, kWhitespaceASCII, TRIM_ALL);
- if (property == "Enrolled via DEP") {
- if (state == "Yes")
- enrolled_via_dep = true;
- else if (state != "No")
- return MacDeviceManagementStateNew::kFailureUnableToParseResult;
- } else if (property == "MDM enrollment") {
- if (state == "Yes")
- mdm_enrollment_not_approved = true;
- else if (state == "Yes (User Approved)")
- mdm_enrollment_user_approved = true;
- else if (state != "No")
- return MacDeviceManagementStateNew::kFailureUnableToParseResult;
- } else {
- // Ignore any other output lines, for future extensibility.
- }
- }
- if (!enrolled_via_dep && !mdm_enrollment_not_approved &&
- !mdm_enrollment_user_approved) {
- return MacDeviceManagementStateNew::kNoEnrollment;
- }
- if (!enrolled_via_dep && mdm_enrollment_not_approved &&
- !mdm_enrollment_user_approved) {
- return MacDeviceManagementStateNew::kLimitedMDMEnrollment;
- }
- if (!enrolled_via_dep && !mdm_enrollment_not_approved &&
- mdm_enrollment_user_approved) {
- return MacDeviceManagementStateNew::kFullMDMEnrollment;
- }
- if (enrolled_via_dep && !mdm_enrollment_not_approved &&
- mdm_enrollment_user_approved) {
- return MacDeviceManagementStateNew::kDEPMDMEnrollment;
- }
- return MacDeviceManagementStateNew::kFailureUnableToParseResult;
- } else {
- return MacDeviceManagementStateNew::kFailureAPIUnavailable;
- }
- }();
- return state;
- }
- DeviceUserDomainJoinState AreDeviceAndUserJoinedToDomain() {
- static DeviceUserDomainJoinState state = [] {
- DeviceUserDomainJoinState state{false, false};
- @autoreleasepool {
- ODSession* session = [ODSession defaultSession];
- if (session == nil) {
- DLOG(WARNING) << "ODSession default session is nil.";
- return state;
- }
- NSError* error = nil;
- NSArray<NSString*>* all_node_names =
- [session nodeNamesAndReturnError:&error];
- if (!all_node_names) {
- DLOG(WARNING) << "ODSession failed to give node names: "
- << error.localizedDescription.UTF8String;
- return state;
- }
- NSUInteger num_nodes = all_node_names.count;
- if (num_nodes < 3) {
- DLOG(WARNING) << "ODSession returned too few node names: "
- << all_node_names.description.UTF8String;
- return state;
- }
- if (num_nodes > 3) {
- // Non-enterprise machines have:"/Search", "/Search/Contacts",
- // "/Local/Default". Everything else would be enterprise management.
- state.device_joined = true;
- }
- ODNode* node = [ODNode nodeWithSession:session
- type:kODNodeTypeAuthentication
- error:&error];
- if (node == nil) {
- DLOG(WARNING) << "ODSession cannot obtain the authentication node: "
- << error.localizedDescription.UTF8String;
- return state;
- }
- // Now check the currently logged on user.
- ODQuery* query = [ODQuery queryWithNode:node
- forRecordTypes:kODRecordTypeUsers
- attribute:kODAttributeTypeRecordName
- matchType:kODMatchEqualTo
- queryValues:NSUserName()
- returnAttributes:kODAttributeTypeAllAttributes
- maximumResults:0
- error:&error];
- if (query == nil) {
- DLOG(WARNING) << "ODSession cannot create user query: "
- << mac::NSToCFCast(error);
- return state;
- }
- NSArray* results = [query resultsAllowingPartial:NO error:&error];
- if (!results) {
- DLOG(WARNING) << "ODSession cannot obtain current user node: "
- << error.localizedDescription.UTF8String;
- return state;
- }
- if (results.count != 1) {
- DLOG(WARNING) << @"ODSession unexpected number of user nodes: "
- << results.count;
- }
- for (id element in results) {
- ODRecord* record = mac::ObjCCastStrict<ODRecord>(element);
- NSArray* attributes =
- [record valuesForAttribute:kODAttributeTypeMetaRecordName
- error:nil];
- for (id attribute in attributes) {
- NSString* attribute_value = mac::ObjCCastStrict<NSString>(attribute);
- // Example: "uid=johnsmith,ou=People,dc=chromium,dc=org
- NSRange domain_controller =
- [attribute_value rangeOfString:@"(^|,)\\s*dc="
- options:NSRegularExpressionSearch];
- if (domain_controller.length > 0) {
- state.user_joined = true;
- }
- }
- // Scan alternative identities.
- attributes =
- [record valuesForAttribute:kODAttributeTypeAltSecurityIdentities
- error:nil];
- for (id attribute in attributes) {
- NSString* attribute_value = mac::ObjCCastStrict<NSString>(attribute);
- NSRange icloud =
- [attribute_value rangeOfString:@"CN=com.apple.idms.appleid.prd"
- options:NSCaseInsensitiveSearch];
- if (!icloud.length) {
- // Any alternative identity that is not iCloud is likely enterprise
- // management.
- state.user_joined = true;
- }
- }
- }
- }
- return state;
- }();
- return state;
- }
- } // namespace base
|