[M85 Dev][EFL] Fix crashes at webview launch
[platform/framework/web/chromium-efl.git] / base / enterprise_util_mac.mm
1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/enterprise_util.h"
6
7 #import <OpenDirectory/OpenDirectory.h>
8
9 #include <string>
10 #include <vector>
11
12 #include "base/logging.h"
13 #include "base/mac/foundation_util.h"
14 #include "base/process/launch.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/sys_string_conversions.h"
18
19 namespace base {
20
21 bool IsMachineExternallyManaged() {
22   DeviceUserDomainJoinState join_state = AreDeviceAndUserJoinedToDomain();
23   return join_state.device_joined || join_state.user_joined;
24 }
25
26 MacDeviceManagementStateOld IsDeviceRegisteredWithManagementOld() {
27   @autoreleasepool {
28     std::vector<std::string> profiler_argv{"/usr/sbin/system_profiler",
29                                            "SPConfigurationProfileDataType",
30                                            "-detailLevel",
31                                            "mini",
32                                            "-timeout",
33                                            "15",
34                                            "-xml"};
35
36     std::string profiler_stdout;
37     if (!GetAppOutput(profiler_argv, &profiler_stdout)) {
38       LOG(WARNING) << "Could not get system_profiler output.";
39       return MacDeviceManagementStateOld::kFailureAPIUnavailable;
40     };
41
42     NSArray* root = base::mac::ObjCCast<NSArray>([NSPropertyListSerialization
43         propertyListWithData:[SysUTF8ToNSString(profiler_stdout)
44                                  dataUsingEncoding:NSUTF8StringEncoding]
45                      options:NSPropertyListImmutable
46                       format:nil
47                        error:nil]);
48     if (!root) {
49       LOG(WARNING) << "Could not parse system_profiler output.";
50       return MacDeviceManagementStateOld::kFailureUnableToParseResult;
51     };
52
53     for (NSDictionary* results in root) {
54       for (NSDictionary* dict in results[@"_items"]) {
55         for (NSDictionary* device_config_profiles in dict[@"_items"]) {
56           for (NSDictionary* profile_item in
57                    device_config_profiles[@"_items"]) {
58             if (![profile_item[@"_name"] isEqual:@"com.apple.mdm"])
59               continue;
60
61             NSString* payload_data =
62                 profile_item[@"spconfigprofile_payload_data"];
63             NSDictionary* payload_data_dict = base::mac::ObjCCast<
64                 NSDictionary>([NSPropertyListSerialization
65                 propertyListWithData:[payload_data
66                                          dataUsingEncoding:NSUTF8StringEncoding]
67                              options:NSPropertyListImmutable
68                               format:nil
69                                error:nil]);
70
71             if (!payload_data_dict)
72               continue;
73
74             // Verify that the URL validates.
75             if ([NSURL URLWithString:payload_data_dict[@"CheckInURL"]])
76               return MacDeviceManagementStateOld::kMDMEnrollment;
77           }
78         }
79       }
80     }
81
82     return MacDeviceManagementStateOld::kNoEnrollment;
83   }
84 }
85
86 MacDeviceManagementStateNew IsDeviceRegisteredWithManagementNew() {
87   if (@available(macOS 10.13.4, *)) {
88     std::vector<std::string> profiles_argv{"/usr/bin/profiles", "status",
89                                            "-type", "enrollment"};
90
91     std::string profiles_stdout;
92     if (!GetAppOutput(profiles_argv, &profiles_stdout)) {
93       LOG(WARNING) << "Could not get profiles output.";
94       return MacDeviceManagementStateNew::kFailureAPIUnavailable;
95     }
96
97     std::vector<StringPiece> lines = SplitStringPiece(
98         profiles_stdout, "\n", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
99
100     bool enrolled_via_dep = false;
101     bool mdm_enrollment_not_approved = false;
102     bool mdm_enrollment_user_approved = false;
103
104     for (const auto& line : lines) {
105       std::vector<StringPiece> halves =
106           SplitStringPiece(line, ":", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
107       if (halves.size() != 2)
108         return MacDeviceManagementStateNew::kFailureUnableToParseResult;
109       StringPiece property = halves[0];
110       StringPiece state = halves[1];
111
112       if (property == "Enrolled via DEP") {
113         if (state == "Yes")
114           enrolled_via_dep = true;
115         else if (state != "No")
116           return MacDeviceManagementStateNew::kFailureUnableToParseResult;
117       } else if (property == "MDM enrollment") {
118         if (state == "Yes")
119           mdm_enrollment_not_approved = true;
120         else if (state == "Yes (User Approved)")
121           mdm_enrollment_user_approved = true;
122         else if (state != "No")
123           return MacDeviceManagementStateNew::kFailureUnableToParseResult;
124       } else {
125         // Ignore any other output lines, for future extensibility.
126       }
127     }
128
129     if (!enrolled_via_dep && !mdm_enrollment_not_approved &&
130         !mdm_enrollment_user_approved) {
131       return MacDeviceManagementStateNew::kNoEnrollment;
132     }
133
134     if (!enrolled_via_dep && mdm_enrollment_not_approved &&
135         !mdm_enrollment_user_approved) {
136       return MacDeviceManagementStateNew::kLimitedMDMEnrollment;
137     }
138
139     if (!enrolled_via_dep && !mdm_enrollment_not_approved &&
140         mdm_enrollment_user_approved) {
141       return MacDeviceManagementStateNew::kFullMDMEnrollment;
142     }
143
144     if (enrolled_via_dep && !mdm_enrollment_not_approved &&
145         mdm_enrollment_user_approved) {
146       return MacDeviceManagementStateNew::kDEPMDMEnrollment;
147     }
148
149     return MacDeviceManagementStateNew::kFailureUnableToParseResult;
150   } else {
151     return MacDeviceManagementStateNew::kFailureAPIUnavailable;
152   }
153 }
154
155 DeviceUserDomainJoinState AreDeviceAndUserJoinedToDomain() {
156   DeviceUserDomainJoinState state{false, false};
157
158   @autoreleasepool {
159     ODSession* session = [ODSession defaultSession];
160     if (session == nil) {
161       DLOG(WARNING) << "ODSession default session is nil.";
162       return state;
163     }
164
165     NSError* error = nil;
166
167     NSArray<NSString*>* all_node_names =
168         [session nodeNamesAndReturnError:&error];
169     if (!all_node_names) {
170       DLOG(WARNING) << "ODSession failed to give node names: "
171                     << error.localizedDescription.UTF8String;
172       return state;
173     }
174
175     NSUInteger num_nodes = all_node_names.count;
176     if (num_nodes < 3) {
177       DLOG(WARNING) << "ODSession returned too few node names: "
178                     << all_node_names.description.UTF8String;
179       return state;
180     }
181
182     if (num_nodes > 3) {
183       // Non-enterprise machines have:"/Search", "/Search/Contacts",
184       // "/Local/Default". Everything else would be enterprise management.
185       state.device_joined = true;
186     }
187
188     ODNode* node = [ODNode nodeWithSession:session
189                                       type:kODNodeTypeAuthentication
190                                      error:&error];
191     if (node == nil) {
192       DLOG(WARNING) << "ODSession cannot obtain the authentication node: "
193                     << error.localizedDescription.UTF8String;
194       return state;
195     }
196
197     // Now check the currently logged on user.
198     ODQuery* query = [ODQuery queryWithNode:node
199                              forRecordTypes:kODRecordTypeUsers
200                                   attribute:kODAttributeTypeRecordName
201                                   matchType:kODMatchEqualTo
202                                 queryValues:NSUserName()
203                            returnAttributes:kODAttributeTypeAllAttributes
204                              maximumResults:0
205                                       error:&error];
206     if (query == nil) {
207       DLOG(WARNING) << "ODSession cannot create user query: "
208                     << mac::NSToCFCast(error);
209       return state;
210     }
211
212     NSArray* results = [query resultsAllowingPartial:NO error:&error];
213     if (!results) {
214       DLOG(WARNING) << "ODSession cannot obtain current user node: "
215                     << error.localizedDescription.UTF8String;
216       return state;
217     }
218
219     if (results.count != 1) {
220       DLOG(WARNING) << @"ODSession unexpected number of user nodes: "
221                     << results.count;
222     }
223
224     for (id element in results) {
225       ODRecord* record = mac::ObjCCastStrict<ODRecord>(element);
226       NSArray* attributes =
227           [record valuesForAttribute:kODAttributeTypeMetaRecordName error:nil];
228       for (id attribute in attributes) {
229         NSString* attribute_value = mac::ObjCCastStrict<NSString>(attribute);
230         // Example: "uid=johnsmith,ou=People,dc=chromium,dc=org
231         NSRange domain_controller =
232             [attribute_value rangeOfString:@"(^|,)\\s*dc="
233                                    options:NSRegularExpressionSearch];
234         if (domain_controller.length > 0) {
235           state.user_joined = true;
236         }
237       }
238
239       // Scan alternative identities.
240       attributes =
241           [record valuesForAttribute:kODAttributeTypeAltSecurityIdentities
242                                error:nil];
243       for (id attribute in attributes) {
244         NSString* attribute_value = mac::ObjCCastStrict<NSString>(attribute);
245         NSRange icloud =
246             [attribute_value rangeOfString:@"CN=com.apple.idms.appleid.prd"
247                                    options:NSCaseInsensitiveSearch];
248         if (!icloud.length) {
249           // Any alternative identity that is not iCloud is likely enterprise
250           // management.
251           state.user_joined = true;
252         }
253       }
254     }
255   }
256
257   return state;
258 }
259
260 }  // namespace base