]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/VPNService.c
configd-888.1.2.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / VPNService.c
1 /*
2 * Copyright (c) 2012, 2013, 2016 Apple Inc. All rights reserved.
3 */
4
5 #include "SCNetworkConfigurationInternal.h"
6 #include "dy_framework.h"
7
8 static CFStringRef g_apple_app_prefix = CFSTR("com.apple.");
9
10 static struct {
11 CFStringRef signing_id;
12 Boolean domains_required;
13 } g_apple_app_exceptions[] = {
14 #if TARGET_OS_IPHONE
15 { CFSTR("com.apple.mobilesafari"), TRUE },
16 { CFSTR("com.apple.webapp"), TRUE },
17 #else
18 { CFSTR("com.apple.WebKit.NetworkProcess"), TRUE },
19 #endif
20 };
21
22
23 static Boolean
24 isA_VPNService(CFTypeRef cf)
25 {
26 if (isA_SCNetworkService(cf)) {
27 SCNetworkInterfaceRef interface = SCNetworkServiceGetInterface((SCNetworkServiceRef)cf);
28
29 return (interface != NULL &&
30 CFEqual(SCNetworkInterfaceGetInterfaceType(interface), kSCNetworkInterfaceTypeVPN));
31 }
32
33 return FALSE;
34 }
35
36
37 static CFArrayRef
38 copy_matching_services(SCPreferencesRef prefs, CFStringRef identifierDomain, CFStringRef identifier)
39 {
40 CFMutableArrayRef results = NULL;
41 CFArrayRef services;
42
43 services = SCNetworkServiceCopyAll(prefs);
44 if (services != NULL) {
45 CFIndex idx;
46 CFIndex service_count = CFArrayGetCount(services);
47
48 for (idx = 0; idx < service_count; idx++) {
49 SCNetworkServiceRef service = CFArrayGetValueAtIndex(services, idx);
50 Boolean matches = FALSE;
51
52 if (isA_VPNService(service)) {
53 if (isA_CFString(identifierDomain) && isA_CFString(identifier)) {
54 CFStringRef ex_identifier = SCNetworkServiceCopyExternalID(service, identifierDomain);
55 if (ex_identifier != NULL) {
56 matches = CFEqual(ex_identifier, identifier);
57 CFRelease(ex_identifier);
58 }
59 } else {
60 matches = TRUE;
61 }
62 }
63
64 if (matches) {
65 if (results == NULL) {
66 results = CFArrayCreateMutable(kCFAllocatorDefault, CFArrayGetCount(services) - idx, &kCFTypeArrayCallBacks);
67 }
68 CFArrayAppendValue(results, service);
69 }
70 }
71
72 CFRelease(services);
73 }
74
75 return results;
76 }
77
78
79 static CFIndex
80 find_app_rule(CFDictionaryRef vpn_config, CFStringRef ruleIdentifier)
81 {
82 CFArrayRef app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
83 CFIndex idx;
84
85 if (isA_CFArray(app_rules)) {
86 CFIndex rule_count = CFArrayGetCount(app_rules);
87
88 for (idx = 0; idx < rule_count; idx++) {
89 CFDictionaryRef rule = CFArrayGetValueAtIndex(app_rules, idx);
90
91 if (isA_CFDictionary(rule)) {
92 CFStringRef rule_id = CFDictionaryGetValue(rule, kSCValNetVPNAppRuleIdentifier);
93
94 if (CFEqual(ruleIdentifier, rule_id)) {
95 return idx;
96 }
97 }
98 }
99 }
100
101 return -1;
102 }
103
104 static Boolean
105 validate_app_rule(CFDictionaryRef ruleSettings, Boolean check_for_apple_apps)
106 {
107 CFIndex account_count = 0;
108 CFArrayRef accounts;
109 CFIndex exception_idx = -1;
110 CFArrayRef executables;
111 CFIndex executable_count = 0;
112 Boolean found_exception = FALSE;
113 CFIndex idx;
114 CFArrayRef match_domains;
115 CFIndex match_domain_count = 0;
116
117 if (!isA_CFDictionary(ruleSettings)) {
118 return FALSE;
119 }
120
121 /* Validate the executable array. It needs to have at least one value. */
122 executables = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleExecutableMatch);
123 if (isA_CFArray(executables) && (executable_count = CFArrayGetCount(executables)) > 0) {
124 for (idx = 0; idx < executable_count; idx++) {
125 CFDictionaryRef executable = CFArrayGetValueAtIndex(executables, idx);
126
127 if (isA_CFDictionary(executable)) {
128 CFStringRef signingID = CFDictionaryGetValue(executable, kSCValNetVPNAppRuleExecutableSigningIdentifier);
129 CFStringRef requirement = CFDictionaryGetValue(executable, kSCValNetVPNAppRuleExecutableDesignatedRequirement);
130
131 if (!isA_CFString(signingID) || CFStringGetLength(signingID) == 0) {
132 return FALSE;
133 }
134
135 if (check_for_apple_apps && CFStringHasPrefix(signingID, g_apple_app_prefix)) {
136 for (exception_idx = 0;
137 exception_idx < sizeof(g_apple_app_exceptions) / sizeof(g_apple_app_exceptions[0]);
138 exception_idx++)
139 {
140 if (CFStringCompare(signingID, g_apple_app_exceptions[exception_idx].signing_id, 0) == 0) {
141 found_exception = TRUE;
142 break;
143 }
144 }
145
146 if (!found_exception) {
147 Boolean can_set_apple_app_rules = FALSE;
148 SecTaskRef current_task = SecTaskCreateFromSelf(kCFAllocatorDefault);
149 if (current_task != NULL) {
150 CFBooleanRef entitlement =
151 SecTaskCopyValueForEntitlement(current_task,
152 CFSTR("com.apple.private.app-vpn-config"),
153 NULL);
154 can_set_apple_app_rules = (isA_CFBoolean(entitlement) && CFBooleanGetValue(entitlement));
155 if (entitlement != NULL) {
156 CFRelease(entitlement);
157 }
158 CFRelease(current_task);
159 }
160 if (!can_set_apple_app_rules) {
161 return FALSE;
162 }
163 }
164 }
165
166 if (requirement != NULL) {
167 if (!isA_CFString(requirement) || CFStringGetLength(requirement) == 0) {
168 return FALSE;
169 }
170 #if !TARGET_OS_IPHONE
171 } else {
172 return FALSE;
173 #endif /* !TARGET_OS_IPHONE */
174 }
175 }
176 }
177 }
178
179 /* Validate the accounts array. It needs to have at least one value. */
180 accounts = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleAccountIdentifierMatch);
181 if (isA_CFArray(accounts) && (account_count = CFArrayGetCount(accounts)) > 0) {
182 for (idx = 0; idx < account_count; idx++) {
183 CFStringRef account = CFArrayGetValueAtIndex(accounts, idx);
184 if (!isA_CFString(account)) {
185 return FALSE;
186 }
187 }
188 }
189
190 /* Either executables or accounts must be present */
191 if (executable_count == 0 && account_count == 0) {
192 return FALSE;
193 }
194
195 /* Validate the domains array. It's optional, so just make sure that it contains only strings if it's present. */
196 match_domains = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleDNSDomainMatch);
197 if (match_domains != NULL) {
198 if (!isA_CFArray(match_domains)) {
199 return FALSE;
200 }
201
202 match_domain_count = CFArrayGetCount(match_domains);
203 for (idx = 0; idx < match_domain_count; idx++) {
204 CFStringRef domain = CFArrayGetValueAtIndex(match_domains, idx);
205 if (!isA_CFString(domain)) {
206 return FALSE;
207 }
208 }
209 }
210
211 /* Require at least one match domain for some Apple apps (like Safari) */
212 if (match_domain_count == 0 &&
213 found_exception &&
214 exception_idx >= 0 &&
215 g_apple_app_exceptions[exception_idx].domains_required)
216 {
217 return FALSE;
218 }
219
220 return TRUE;
221 }
222
223
224 CFArrayRef
225 VPNServiceCopyAllMatchingExternalID(SCPreferencesRef prefs, CFStringRef identifierDomain, CFStringRef identifier)
226 {
227 CFArrayRef services;
228
229 if (prefs == NULL || !isA_CFString(identifierDomain) || !isA_CFString(identifier)) {
230 _SCErrorSet(kSCStatusInvalidArgument);
231 return NULL;
232 }
233
234 services = copy_matching_services(prefs, identifierDomain, identifier);
235 if (services == NULL) {
236 _SCErrorSet(kSCStatusOK);
237 }
238
239 return services;
240 }
241
242
243 CFArrayRef
244 VPNServiceCopyAll(SCPreferencesRef prefs)
245 {
246 CFArrayRef services;
247
248 if (prefs == NULL) {
249 _SCErrorSet(kSCStatusInvalidArgument);
250 return NULL;
251 }
252
253 services = copy_matching_services(prefs, NULL, NULL);
254 if (services == NULL) {
255 _SCErrorSet(kSCStatusOK);
256 }
257
258 return services;
259 }
260
261
262 CFArrayRef
263 VPNServiceCopyAppRuleIDs(VPNServiceRef service)
264 {
265 CFMutableArrayRef results = NULL;
266 CFDictionaryRef vpn_config;
267
268 if (!isA_VPNService(service)) {
269 _SCErrorSet(kSCStatusInvalidArgument);
270 return NULL;
271 }
272
273 vpn_config = SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service));
274
275 if (isA_CFDictionary(vpn_config)) {
276 CFArrayRef app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
277 if (isA_CFArray(app_rules)) {
278 CFIndex app_rule_count = CFArrayGetCount(app_rules);
279 CFIndex idx;
280 results = CFArrayCreateMutable(kCFAllocatorDefault, app_rule_count, &kCFTypeArrayCallBacks);
281 for (idx = 0; idx < app_rule_count; idx++) {
282 CFDictionaryRef rule = CFArrayGetValueAtIndex(app_rules, idx);
283 if (isA_CFDictionary(rule)) {
284 CFStringRef rule_ID = CFDictionaryGetValue(rule, kSCValNetVPNAppRuleIdentifier);
285 if (isA_CFString(rule_ID)) {
286 CFArrayAppendValue(results, CFDictionaryGetValue(rule, kSCValNetVPNAppRuleIdentifier));
287 }
288 }
289 }
290 if (CFArrayGetCount(results) == 0) {
291 CFRelease(results);
292 results = NULL;
293 }
294 }
295 }
296
297 if (results == NULL) {
298 _SCErrorSet(kSCStatusOK);
299 }
300
301 return results;
302 }
303
304
305 Boolean
306 VPNServiceSetAppRule(VPNServiceRef service, CFStringRef ruleIdentifier, CFDictionaryRef ruleSettings)
307 {
308 CFArrayRef accounts;
309 CFArrayRef app_rules;
310 CFArrayRef executables;
311 CFIndex existing_idx = -1;
312 CFArrayRef match_domains;
313 CFMutableArrayRef new_app_rules;
314 CFMutableDictionaryRef new_settings;
315 CFMutableDictionaryRef new_vpn_config;
316 CFDictionaryRef vpn_config;
317
318 /* Basic parameter validation */
319
320 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
321 _SCErrorSet(kSCStatusInvalidArgument);
322 return FALSE;
323 }
324
325 if (!validate_app_rule(ruleSettings, TRUE)) {
326 _SCErrorSet(kSCStatusInvalidArgument);
327 return FALSE;
328 }
329
330 executables = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleExecutableMatch);
331 match_domains = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleDNSDomainMatch);
332 accounts = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleAccountIdentifierMatch);
333
334 /* Set the new rule config, replacing any existing rule */
335
336 vpn_config = SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service));
337 if (isA_CFDictionary(vpn_config)) {
338 existing_idx = find_app_rule(vpn_config, ruleIdentifier);
339 new_vpn_config = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, vpn_config);
340 } else {
341 new_vpn_config = CFDictionaryCreateMutable(kCFAllocatorDefault,
342 0,
343 &kCFTypeDictionaryKeyCallBacks,
344 &kCFTypeDictionaryValueCallBacks);
345 }
346
347 app_rules = CFDictionaryGetValue(new_vpn_config, kSCPropNetVPNAppRules);
348 if (isA_CFArray(app_rules)) {
349 new_app_rules = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, app_rules);
350 } else {
351 new_app_rules = CFArrayCreateMutable(kCFAllocatorDefault,
352 0,
353 &kCFTypeArrayCallBacks);
354 }
355
356 new_settings = CFDictionaryCreateMutable(kCFAllocatorDefault,
357 0,
358 &kCFTypeDictionaryKeyCallBacks,
359 &kCFTypeDictionaryValueCallBacks);
360
361 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleIdentifier, ruleIdentifier);
362 if (executables != NULL && CFArrayGetCount(executables) > 0) {
363 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleExecutableMatch, executables);
364 }
365 if (match_domains != NULL && CFArrayGetCount(match_domains) > 0) {
366 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleDNSDomainMatch, match_domains);
367 }
368 if (accounts != NULL && CFArrayGetCount(accounts) > 0) {
369 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleAccountIdentifierMatch, accounts);
370 }
371
372 if (existing_idx >= 0) {
373 CFArraySetValueAtIndex(new_app_rules, existing_idx, new_settings);
374 } else {
375 CFArrayAppendValue(new_app_rules, new_settings);
376 }
377
378 CFDictionarySetValue(new_vpn_config, kSCPropNetVPNAppRules, new_app_rules);
379
380 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service), new_vpn_config);
381
382 CFRelease(new_vpn_config);
383 CFRelease(new_app_rules);
384 CFRelease(new_settings);
385
386 return TRUE;
387 }
388
389
390 CFDictionaryRef
391 VPNServiceCopyAppRule(VPNServiceRef service, CFStringRef ruleIdentifier)
392 {
393 CFDictionaryRef vpn_config;
394
395 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
396 _SCErrorSet(kSCStatusInvalidArgument);
397 return NULL;
398 }
399
400 vpn_config = SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service));
401 if (isA_CFDictionary(vpn_config)) {
402 CFIndex idx = find_app_rule(vpn_config, ruleIdentifier);
403 if (idx >= 0) {
404 CFArrayRef app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
405 CFDictionaryRef ruleSettings = CFArrayGetValueAtIndex(app_rules, idx);
406
407 if (validate_app_rule(ruleSettings, FALSE)) {
408 return (CFDictionaryRef)CFRetain(ruleSettings);
409 } else {
410 _SCErrorSet(kSCStatusFailed);
411 }
412 } else {
413 _SCErrorSet(kSCStatusNoKey);
414 }
415 } else {
416 _SCErrorSet(kSCStatusFailed);
417 }
418
419 return NULL;
420 }
421
422
423 Boolean
424 VPNServiceRemoveAppRule(VPNServiceRef service, CFStringRef ruleIdentifier)
425 {
426 CFDictionaryRef vpn_config;
427
428 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
429 _SCErrorSet(kSCStatusInvalidArgument);
430 return FALSE;
431 }
432
433 vpn_config = SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service));
434 if (isA_CFDictionary(vpn_config)) {
435 CFIndex idx = find_app_rule(vpn_config, ruleIdentifier);
436 if (idx >= 0) {
437 CFArrayRef current_app_rules;
438 current_app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
439 if (isA_CFArray(current_app_rules)) {
440 CFMutableDictionaryRef new_vpn_config;
441 CFMutableArrayRef new_app_rules;
442
443 new_vpn_config = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, vpn_config);
444 new_app_rules = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, current_app_rules);
445
446 CFArrayRemoveValueAtIndex(new_app_rules, idx);
447 if (CFArrayGetCount(new_app_rules) > 0) {
448 CFDictionarySetValue(new_vpn_config, kSCPropNetVPNAppRules, new_app_rules);
449 } else {
450 CFDictionaryRemoveValue(new_vpn_config, kSCPropNetVPNAppRules);
451 }
452
453 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service), new_vpn_config);
454
455 CFRelease(new_vpn_config);
456 CFRelease(new_app_rules);
457
458 return TRUE;
459 } else {
460 _SCErrorSet(kSCStatusFailed);
461 }
462 } else {
463 _SCErrorSet(kSCStatusNoKey);
464 }
465 } else {
466 _SCErrorSet(kSCStatusFailed);
467 }
468
469 return FALSE;
470 }
471
472
473 Boolean
474 VPNServiceIsManagedAppVPN(VPNServiceRef service)
475 {
476 Boolean result = FALSE;
477 CFStringRef mc_external_id = SCNetworkServiceCopyExternalID(service, CFSTR("MCVPNUUID"));
478 if (isA_CFString(mc_external_id)) {
479 result = TRUE;
480 }
481 if (mc_external_id != NULL) {
482 CFRelease(mc_external_id);
483 }
484 return result;
485 }
486