fixup! [M120 Migration] Notify media device state to webbrowser
[platform/framework/web/chromium-efl.git] / base / enterprise_util_mac.mm
1 // Copyright 2019 The Chromium Authors
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/apple/foundation_util.h"
13 #include "base/logging.h"
14 #include "base/process/launch.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/sys_string_conversions.h"
18
19 namespace base {
20
21 bool IsManagedDevice() {
22   // MDM enrollment indicates the device is actively being managed. Simply being
23   // joined to a domain, however, does not.
24   base::MacDeviceManagementState mdm_state =
25       base::IsDeviceRegisteredWithManagement();
26   return mdm_state == base::MacDeviceManagementState::kLimitedMDMEnrollment ||
27          mdm_state == base::MacDeviceManagementState::kFullMDMEnrollment ||
28          mdm_state == base::MacDeviceManagementState::kDEPMDMEnrollment;
29 }
30
31 bool IsEnterpriseDevice() {
32   // Domain join is a basic indicator of being an enterprise device.
33   DeviceUserDomainJoinState join_state = AreDeviceAndUserJoinedToDomain();
34   return join_state.device_joined || join_state.user_joined;
35 }
36
37 MacDeviceManagementState IsDeviceRegisteredWithManagement() {
38   static MacDeviceManagementState state = [] {
39     std::vector<std::string> profiles_argv{"/usr/bin/profiles", "status",
40                                            "-type", "enrollment"};
41
42     std::string profiles_stdout;
43     if (!GetAppOutput(profiles_argv, &profiles_stdout)) {
44       LOG(WARNING) << "Could not get profiles output.";
45       return MacDeviceManagementState::kFailureAPIUnavailable;
46     }
47
48     // Sample output of `profiles` with full MDM enrollment:
49     // Enrolled via DEP: Yes
50     // MDM enrollment: Yes (User Approved)
51     // MDM server: https://applemdm.example.com/some/path?foo=bar
52     StringPairs property_states;
53     if (!SplitStringIntoKeyValuePairs(profiles_stdout, ':', '\n',
54                                       &property_states)) {
55       return MacDeviceManagementState::kFailureUnableToParseResult;
56     }
57
58     bool enrolled_via_dep = false;
59     bool mdm_enrollment_not_approved = false;
60     bool mdm_enrollment_user_approved = false;
61
62     for (const auto& property_state : property_states) {
63       StringPiece property =
64           TrimString(property_state.first, kWhitespaceASCII, TRIM_ALL);
65       StringPiece state =
66           TrimString(property_state.second, kWhitespaceASCII, TRIM_ALL);
67
68       if (property == "Enrolled via DEP") {
69         if (state == "Yes") {
70           enrolled_via_dep = true;
71         } else if (state != "No") {
72           return MacDeviceManagementState::kFailureUnableToParseResult;
73         }
74       } else if (property == "MDM enrollment") {
75         if (state == "Yes") {
76           mdm_enrollment_not_approved = true;
77         } else if (state == "Yes (User Approved)") {
78           mdm_enrollment_user_approved = true;
79         } else if (state != "No") {
80           return MacDeviceManagementState::kFailureUnableToParseResult;
81         }
82       } else {
83         // Ignore any other output lines, for future extensibility.
84       }
85     }
86
87     if (!enrolled_via_dep && !mdm_enrollment_not_approved &&
88         !mdm_enrollment_user_approved) {
89       return MacDeviceManagementState::kNoEnrollment;
90     }
91
92     if (!enrolled_via_dep && mdm_enrollment_not_approved &&
93         !mdm_enrollment_user_approved) {
94       return MacDeviceManagementState::kLimitedMDMEnrollment;
95     }
96
97     if (!enrolled_via_dep && !mdm_enrollment_not_approved &&
98         mdm_enrollment_user_approved) {
99       return MacDeviceManagementState::kFullMDMEnrollment;
100     }
101
102     if (enrolled_via_dep && !mdm_enrollment_not_approved &&
103         mdm_enrollment_user_approved) {
104       return MacDeviceManagementState::kDEPMDMEnrollment;
105     }
106
107     return MacDeviceManagementState::kFailureUnableToParseResult;
108   }();
109
110   return state;
111 }
112
113 DeviceUserDomainJoinState AreDeviceAndUserJoinedToDomain() {
114   static DeviceUserDomainJoinState state = [] {
115     DeviceUserDomainJoinState state{false, false};
116
117     @autoreleasepool {
118       ODSession* session = [ODSession defaultSession];
119       if (session == nil) {
120         DLOG(WARNING) << "ODSession default session is nil.";
121         return state;
122       }
123
124       NSError* error = nil;
125
126       NSArray<NSString*>* all_node_names =
127           [session nodeNamesAndReturnError:&error];
128       if (!all_node_names) {
129         DLOG(WARNING) << "ODSession failed to give node names: "
130                       << error.localizedDescription.UTF8String;
131         return state;
132       }
133
134       NSUInteger num_nodes = all_node_names.count;
135       if (num_nodes < 3) {
136         DLOG(WARNING) << "ODSession returned too few node names: "
137                       << all_node_names.description.UTF8String;
138         return state;
139       }
140
141       if (num_nodes > 3) {
142         // Non-enterprise machines have:"/Search", "/Search/Contacts",
143         // "/Local/Default". Everything else would be enterprise management.
144         state.device_joined = true;
145       }
146
147       ODNode* node = [ODNode nodeWithSession:session
148                                         type:kODNodeTypeAuthentication
149                                        error:&error];
150       if (node == nil) {
151         DLOG(WARNING) << "ODSession cannot obtain the authentication node: "
152                       << error.localizedDescription.UTF8String;
153         return state;
154       }
155
156       // Now check the currently logged on user.
157       ODQuery* query = [ODQuery queryWithNode:node
158                                forRecordTypes:kODRecordTypeUsers
159                                     attribute:kODAttributeTypeRecordName
160                                     matchType:kODMatchEqualTo
161                                   queryValues:NSUserName()
162                              returnAttributes:kODAttributeTypeAllAttributes
163                                maximumResults:0
164                                         error:&error];
165       if (query == nil) {
166         DLOG(WARNING) << "ODSession cannot create user query: "
167                       << error.localizedDescription.UTF8String;
168         return state;
169       }
170
171       NSArray* results = [query resultsAllowingPartial:NO error:&error];
172       if (!results) {
173         DLOG(WARNING) << "ODSession cannot obtain current user node: "
174                       << error.localizedDescription.UTF8String;
175         return state;
176       }
177
178       if (results.count != 1) {
179         DLOG(WARNING) << @"ODSession unexpected number of user nodes: "
180                       << results.count;
181       }
182
183       for (id element in results) {
184         ODRecord* record = base::apple::ObjCCastStrict<ODRecord>(element);
185         NSArray* attributes =
186             [record valuesForAttribute:kODAttributeTypeMetaRecordName
187                                  error:nil];
188         for (id attribute in attributes) {
189           NSString* attribute_value =
190               base::apple::ObjCCastStrict<NSString>(attribute);
191           // Example: "uid=johnsmith,ou=People,dc=chromium,dc=org
192           NSRange domain_controller =
193               [attribute_value rangeOfString:@"(^|,)\\s*dc="
194                                      options:NSRegularExpressionSearch];
195           if (domain_controller.length > 0) {
196             state.user_joined = true;
197           }
198         }
199
200         // Scan alternative identities.
201         attributes =
202             [record valuesForAttribute:kODAttributeTypeAltSecurityIdentities
203                                  error:nil];
204         for (id attribute in attributes) {
205           NSString* attribute_value =
206               base::apple::ObjCCastStrict<NSString>(attribute);
207           NSRange icloud =
208               [attribute_value rangeOfString:@"CN=com.apple.idms.appleid.prd"
209                                      options:NSCaseInsensitiveSearch];
210           if (!icloud.length) {
211             // Any alternative identity that is not iCloud is likely enterprise
212             // management.
213             state.user_joined = true;
214           }
215         }
216       }
217     }
218
219     return state;
220   }();
221
222   return state;
223 }
224
225 }  // namespace base