]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/VPNService.c
configd-1061.0.2.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / VPNService.c
1 /*
2 * Copyright (c) 2012, 2013, 2016, 2017 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 < (CFIndex)(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 SCNetworkInterfaceRef interface;
266 CFMutableArrayRef results = NULL;
267 CFDictionaryRef vpn_config;
268
269 if (!isA_VPNService(service)) {
270 _SCErrorSet(kSCStatusInvalidArgument);
271 return NULL;
272 }
273
274 interface = SCNetworkServiceGetInterface(service);
275 if (interface == NULL) {
276 _SCErrorSet(kSCStatusInvalidArgument);
277 return NULL;
278 }
279
280 vpn_config = SCNetworkInterfaceGetConfiguration(interface);
281 if (isA_CFDictionary(vpn_config)) {
282 CFArrayRef app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
283 if (isA_CFArray(app_rules)) {
284 CFIndex app_rule_count = CFArrayGetCount(app_rules);
285 CFIndex idx;
286 results = CFArrayCreateMutable(kCFAllocatorDefault, app_rule_count, &kCFTypeArrayCallBacks);
287 for (idx = 0; idx < app_rule_count; idx++) {
288 CFDictionaryRef rule = CFArrayGetValueAtIndex(app_rules, idx);
289 if (isA_CFDictionary(rule)) {
290 CFStringRef rule_ID = CFDictionaryGetValue(rule, kSCValNetVPNAppRuleIdentifier);
291 if (isA_CFString(rule_ID)) {
292 CFArrayAppendValue(results, CFDictionaryGetValue(rule, kSCValNetVPNAppRuleIdentifier));
293 }
294 }
295 }
296 if (CFArrayGetCount(results) == 0) {
297 CFRelease(results);
298 results = NULL;
299 }
300 }
301 }
302
303 if (results == NULL) {
304 _SCErrorSet(kSCStatusOK);
305 }
306
307 return results;
308 }
309
310
311 Boolean
312 VPNServiceSetAppRule(VPNServiceRef service, CFStringRef ruleIdentifier, CFDictionaryRef ruleSettings)
313 {
314 CFArrayRef accounts;
315 CFArrayRef app_rules;
316 CFArrayRef executables;
317 CFIndex existing_idx = -1;
318 SCNetworkInterfaceRef interface;
319 CFArrayRef match_domains;
320 CFMutableArrayRef new_app_rules;
321 CFMutableDictionaryRef new_settings;
322 CFMutableDictionaryRef new_vpn_config;
323 CFDictionaryRef vpn_config;
324
325 /* Basic parameter validation */
326
327 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
328 _SCErrorSet(kSCStatusInvalidArgument);
329 return FALSE;
330 }
331
332 if (!validate_app_rule(ruleSettings, TRUE)) {
333 _SCErrorSet(kSCStatusInvalidArgument);
334 return FALSE;
335 }
336
337 interface = SCNetworkServiceGetInterface(service);
338 if (interface == NULL) {
339 _SCErrorSet(kSCStatusInvalidArgument);
340 return FALSE;
341 }
342
343 executables = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleExecutableMatch);
344 match_domains = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleDNSDomainMatch);
345 accounts = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleAccountIdentifierMatch);
346
347 /* Set the new rule config, replacing any existing rule */
348
349 vpn_config = SCNetworkInterfaceGetConfiguration(interface);
350 if (isA_CFDictionary(vpn_config)) {
351 existing_idx = find_app_rule(vpn_config, ruleIdentifier);
352 new_vpn_config = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, vpn_config);
353 } else {
354 new_vpn_config = CFDictionaryCreateMutable(kCFAllocatorDefault,
355 0,
356 &kCFTypeDictionaryKeyCallBacks,
357 &kCFTypeDictionaryValueCallBacks);
358 }
359
360 app_rules = CFDictionaryGetValue(new_vpn_config, kSCPropNetVPNAppRules);
361 if (isA_CFArray(app_rules)) {
362 new_app_rules = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, app_rules);
363 } else {
364 new_app_rules = CFArrayCreateMutable(kCFAllocatorDefault,
365 0,
366 &kCFTypeArrayCallBacks);
367 }
368
369 new_settings = CFDictionaryCreateMutable(kCFAllocatorDefault,
370 0,
371 &kCFTypeDictionaryKeyCallBacks,
372 &kCFTypeDictionaryValueCallBacks);
373
374 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleIdentifier, ruleIdentifier);
375 if (executables != NULL && CFArrayGetCount(executables) > 0) {
376 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleExecutableMatch, executables);
377 }
378 if (match_domains != NULL && CFArrayGetCount(match_domains) > 0) {
379 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleDNSDomainMatch, match_domains);
380 }
381 if (accounts != NULL && CFArrayGetCount(accounts) > 0) {
382 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleAccountIdentifierMatch, accounts);
383 }
384
385 if (existing_idx >= 0) {
386 CFArraySetValueAtIndex(new_app_rules, existing_idx, new_settings);
387 } else {
388 CFArrayAppendValue(new_app_rules, new_settings);
389 }
390
391 CFDictionarySetValue(new_vpn_config, kSCPropNetVPNAppRules, new_app_rules);
392
393 SCNetworkInterfaceSetConfiguration(interface, new_vpn_config);
394
395 CFRelease(new_vpn_config);
396 CFRelease(new_app_rules);
397 CFRelease(new_settings);
398
399 return TRUE;
400 }
401
402
403 CFDictionaryRef
404 VPNServiceCopyAppRule(VPNServiceRef service, CFStringRef ruleIdentifier)
405 {
406 SCNetworkInterfaceRef interface;
407 CFDictionaryRef vpn_config;
408
409 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
410 _SCErrorSet(kSCStatusInvalidArgument);
411 return NULL;
412 }
413
414 interface = SCNetworkServiceGetInterface(service);
415 if (interface == NULL) {
416 _SCErrorSet(kSCStatusInvalidArgument);
417 return FALSE;
418 }
419
420 vpn_config = SCNetworkInterfaceGetConfiguration(interface);
421 if (isA_CFDictionary(vpn_config)) {
422 CFIndex idx = find_app_rule(vpn_config, ruleIdentifier);
423 if (idx >= 0) {
424 CFArrayRef app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
425 CFDictionaryRef ruleSettings = CFArrayGetValueAtIndex(app_rules, idx);
426
427 if (validate_app_rule(ruleSettings, FALSE)) {
428 return (CFDictionaryRef)CFRetain(ruleSettings);
429 } else {
430 _SCErrorSet(kSCStatusFailed);
431 }
432 } else {
433 _SCErrorSet(kSCStatusNoKey);
434 }
435 } else {
436 _SCErrorSet(kSCStatusFailed);
437 }
438
439 return NULL;
440 }
441
442
443 Boolean
444 VPNServiceRemoveAppRule(VPNServiceRef service, CFStringRef ruleIdentifier)
445 {
446 SCNetworkInterfaceRef interface;
447 CFDictionaryRef vpn_config;
448
449 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
450 _SCErrorSet(kSCStatusInvalidArgument);
451 return FALSE;
452 }
453
454 interface = SCNetworkServiceGetInterface(service);
455 if (interface == NULL) {
456 _SCErrorSet(kSCStatusInvalidArgument);
457 return FALSE;
458 }
459
460 vpn_config = SCNetworkInterfaceGetConfiguration(interface);
461 if (isA_CFDictionary(vpn_config)) {
462 CFIndex idx = find_app_rule(vpn_config, ruleIdentifier);
463 if (idx >= 0) {
464 CFArrayRef current_app_rules;
465 current_app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
466 if (isA_CFArray(current_app_rules)) {
467 CFMutableDictionaryRef new_vpn_config;
468 CFMutableArrayRef new_app_rules;
469
470 new_vpn_config = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, vpn_config);
471 new_app_rules = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, current_app_rules);
472
473 CFArrayRemoveValueAtIndex(new_app_rules, idx);
474 if (CFArrayGetCount(new_app_rules) > 0) {
475 CFDictionarySetValue(new_vpn_config, kSCPropNetVPNAppRules, new_app_rules);
476 } else {
477 CFDictionaryRemoveValue(new_vpn_config, kSCPropNetVPNAppRules);
478 }
479
480 SCNetworkInterfaceSetConfiguration(interface, new_vpn_config);
481
482 CFRelease(new_vpn_config);
483 CFRelease(new_app_rules);
484
485 return TRUE;
486 } else {
487 _SCErrorSet(kSCStatusFailed);
488 }
489 } else {
490 _SCErrorSet(kSCStatusNoKey);
491 }
492 } else {
493 _SCErrorSet(kSCStatusFailed);
494 }
495
496 return FALSE;
497 }
498
499
500 Boolean
501 VPNServiceIsManagedAppVPN(VPNServiceRef service)
502 {
503 Boolean result = FALSE;
504 CFStringRef mc_external_id = SCNetworkServiceCopyExternalID(service, CFSTR("MCVPNUUID"));
505 if (isA_CFString(mc_external_id)) {
506 result = TRUE;
507 }
508 if (mc_external_id != NULL) {
509 CFRelease(mc_external_id);
510 }
511 return result;
512 }
513