2 * Copyright (c) 2012, 2013, 2016, 2017 Apple Inc. All rights reserved.
5 #include "SCNetworkConfigurationInternal.h"
6 #include "dy_framework.h"
8 static CFStringRef g_apple_app_prefix
= CFSTR("com.apple.");
11 CFStringRef signing_id
;
12 Boolean domains_required
;
13 } g_apple_app_exceptions
[] = {
15 { CFSTR("com.apple.mobilesafari"), TRUE
},
16 { CFSTR("com.apple.webapp"), TRUE
},
18 { CFSTR("com.apple.WebKit.NetworkProcess"), TRUE
},
24 isA_VPNService(CFTypeRef cf
)
26 if (isA_SCNetworkService(cf
)) {
27 SCNetworkInterfaceRef interface
= SCNetworkServiceGetInterface((SCNetworkServiceRef
)cf
);
29 return (interface
!= NULL
&&
30 CFEqual(SCNetworkInterfaceGetInterfaceType(interface
), kSCNetworkInterfaceTypeVPN
));
38 copy_matching_services(SCPreferencesRef prefs
, CFStringRef identifierDomain
, CFStringRef identifier
)
40 CFMutableArrayRef results
= NULL
;
43 services
= SCNetworkServiceCopyAll(prefs
);
44 if (services
!= NULL
) {
46 CFIndex service_count
= CFArrayGetCount(services
);
48 for (idx
= 0; idx
< service_count
; idx
++) {
49 SCNetworkServiceRef service
= CFArrayGetValueAtIndex(services
, idx
);
50 Boolean matches
= FALSE
;
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
);
65 if (results
== NULL
) {
66 results
= CFArrayCreateMutable(kCFAllocatorDefault
, CFArrayGetCount(services
) - idx
, &kCFTypeArrayCallBacks
);
68 CFArrayAppendValue(results
, service
);
80 find_app_rule(CFDictionaryRef vpn_config
, CFStringRef ruleIdentifier
)
82 CFArrayRef app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
85 if (isA_CFArray(app_rules
)) {
86 CFIndex rule_count
= CFArrayGetCount(app_rules
);
88 for (idx
= 0; idx
< rule_count
; idx
++) {
89 CFDictionaryRef rule
= CFArrayGetValueAtIndex(app_rules
, idx
);
91 if (isA_CFDictionary(rule
)) {
92 CFStringRef rule_id
= CFDictionaryGetValue(rule
, kSCValNetVPNAppRuleIdentifier
);
94 if (CFEqual(ruleIdentifier
, rule_id
)) {
105 validate_app_rule(CFDictionaryRef ruleSettings
, Boolean check_for_apple_apps
)
107 CFIndex account_count
= 0;
109 CFIndex exception_idx
= -1;
110 CFArrayRef executables
;
111 CFIndex executable_count
= 0;
112 Boolean found_exception
= FALSE
;
114 CFArrayRef match_domains
;
115 CFIndex match_domain_count
= 0;
117 if (!isA_CFDictionary(ruleSettings
)) {
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
);
127 if (isA_CFDictionary(executable
)) {
128 CFStringRef signingID
= CFDictionaryGetValue(executable
, kSCValNetVPNAppRuleExecutableSigningIdentifier
);
129 CFStringRef requirement
= CFDictionaryGetValue(executable
, kSCValNetVPNAppRuleExecutableDesignatedRequirement
);
131 if (!isA_CFString(signingID
) || CFStringGetLength(signingID
) == 0) {
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]));
140 if (CFStringCompare(signingID
, g_apple_app_exceptions
[exception_idx
].signing_id
, 0) == 0) {
141 found_exception
= TRUE
;
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"),
154 can_set_apple_app_rules
= (isA_CFBoolean(entitlement
) && CFBooleanGetValue(entitlement
));
155 if (entitlement
!= NULL
) {
156 CFRelease(entitlement
);
158 CFRelease(current_task
);
160 if (!can_set_apple_app_rules
) {
166 if (requirement
!= NULL
) {
167 if (!isA_CFString(requirement
) || CFStringGetLength(requirement
) == 0) {
170 #if !TARGET_OS_IPHONE
173 #endif /* !TARGET_OS_IPHONE */
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
)) {
190 /* Either executables or accounts must be present */
191 if (executable_count
== 0 && account_count
== 0) {
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
)) {
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
)) {
211 /* Require at least one match domain for some Apple apps (like Safari) */
212 if (match_domain_count
== 0 &&
214 exception_idx
>= 0 &&
215 g_apple_app_exceptions
[exception_idx
].domains_required
)
225 VPNServiceCopyAllMatchingExternalID(SCPreferencesRef prefs
, CFStringRef identifierDomain
, CFStringRef identifier
)
229 if (prefs
== NULL
|| !isA_CFString(identifierDomain
) || !isA_CFString(identifier
)) {
230 _SCErrorSet(kSCStatusInvalidArgument
);
234 services
= copy_matching_services(prefs
, identifierDomain
, identifier
);
235 if (services
== NULL
) {
236 _SCErrorSet(kSCStatusOK
);
244 VPNServiceCopyAll(SCPreferencesRef prefs
)
249 _SCErrorSet(kSCStatusInvalidArgument
);
253 services
= copy_matching_services(prefs
, NULL
, NULL
);
254 if (services
== NULL
) {
255 _SCErrorSet(kSCStatusOK
);
263 VPNServiceCopyAppRuleIDs(VPNServiceRef service
)
265 SCNetworkInterfaceRef interface
;
266 CFMutableArrayRef results
= NULL
;
267 CFDictionaryRef vpn_config
;
269 if (!isA_VPNService(service
)) {
270 _SCErrorSet(kSCStatusInvalidArgument
);
274 interface
= SCNetworkServiceGetInterface(service
);
275 if (interface
== NULL
) {
276 _SCErrorSet(kSCStatusInvalidArgument
);
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
);
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
));
296 if (CFArrayGetCount(results
) == 0) {
303 if (results
== NULL
) {
304 _SCErrorSet(kSCStatusOK
);
312 VPNServiceSetAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
, CFDictionaryRef ruleSettings
)
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
;
325 /* Basic parameter validation */
327 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
328 _SCErrorSet(kSCStatusInvalidArgument
);
332 if (!validate_app_rule(ruleSettings
, TRUE
)) {
333 _SCErrorSet(kSCStatusInvalidArgument
);
337 interface
= SCNetworkServiceGetInterface(service
);
338 if (interface
== NULL
) {
339 _SCErrorSet(kSCStatusInvalidArgument
);
343 executables
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleExecutableMatch
);
344 match_domains
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleDNSDomainMatch
);
345 accounts
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleAccountIdentifierMatch
);
347 /* Set the new rule config, replacing any existing rule */
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
);
354 new_vpn_config
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
356 &kCFTypeDictionaryKeyCallBacks
,
357 &kCFTypeDictionaryValueCallBacks
);
360 app_rules
= CFDictionaryGetValue(new_vpn_config
, kSCPropNetVPNAppRules
);
361 if (isA_CFArray(app_rules
)) {
362 new_app_rules
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, app_rules
);
364 new_app_rules
= CFArrayCreateMutable(kCFAllocatorDefault
,
366 &kCFTypeArrayCallBacks
);
369 new_settings
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
371 &kCFTypeDictionaryKeyCallBacks
,
372 &kCFTypeDictionaryValueCallBacks
);
374 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleIdentifier
, ruleIdentifier
);
375 if (executables
!= NULL
&& CFArrayGetCount(executables
) > 0) {
376 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleExecutableMatch
, executables
);
378 if (match_domains
!= NULL
&& CFArrayGetCount(match_domains
) > 0) {
379 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleDNSDomainMatch
, match_domains
);
381 if (accounts
!= NULL
&& CFArrayGetCount(accounts
) > 0) {
382 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleAccountIdentifierMatch
, accounts
);
385 if (existing_idx
>= 0) {
386 CFArraySetValueAtIndex(new_app_rules
, existing_idx
, new_settings
);
388 CFArrayAppendValue(new_app_rules
, new_settings
);
391 CFDictionarySetValue(new_vpn_config
, kSCPropNetVPNAppRules
, new_app_rules
);
393 SCNetworkInterfaceSetConfiguration(interface
, new_vpn_config
);
395 CFRelease(new_vpn_config
);
396 CFRelease(new_app_rules
);
397 CFRelease(new_settings
);
404 VPNServiceCopyAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
)
406 SCNetworkInterfaceRef interface
;
407 CFDictionaryRef vpn_config
;
409 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
410 _SCErrorSet(kSCStatusInvalidArgument
);
414 interface
= SCNetworkServiceGetInterface(service
);
415 if (interface
== NULL
) {
416 _SCErrorSet(kSCStatusInvalidArgument
);
420 vpn_config
= SCNetworkInterfaceGetConfiguration(interface
);
421 if (isA_CFDictionary(vpn_config
)) {
422 CFIndex idx
= find_app_rule(vpn_config
, ruleIdentifier
);
424 CFArrayRef app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
425 CFDictionaryRef ruleSettings
= CFArrayGetValueAtIndex(app_rules
, idx
);
427 if (validate_app_rule(ruleSettings
, FALSE
)) {
428 return (CFDictionaryRef
)CFRetain(ruleSettings
);
430 _SCErrorSet(kSCStatusFailed
);
433 _SCErrorSet(kSCStatusNoKey
);
436 _SCErrorSet(kSCStatusFailed
);
444 VPNServiceRemoveAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
)
446 SCNetworkInterfaceRef interface
;
447 CFDictionaryRef vpn_config
;
449 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
450 _SCErrorSet(kSCStatusInvalidArgument
);
454 interface
= SCNetworkServiceGetInterface(service
);
455 if (interface
== NULL
) {
456 _SCErrorSet(kSCStatusInvalidArgument
);
460 vpn_config
= SCNetworkInterfaceGetConfiguration(interface
);
461 if (isA_CFDictionary(vpn_config
)) {
462 CFIndex idx
= find_app_rule(vpn_config
, ruleIdentifier
);
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
;
470 new_vpn_config
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, vpn_config
);
471 new_app_rules
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, current_app_rules
);
473 CFArrayRemoveValueAtIndex(new_app_rules
, idx
);
474 if (CFArrayGetCount(new_app_rules
) > 0) {
475 CFDictionarySetValue(new_vpn_config
, kSCPropNetVPNAppRules
, new_app_rules
);
477 CFDictionaryRemoveValue(new_vpn_config
, kSCPropNetVPNAppRules
);
480 SCNetworkInterfaceSetConfiguration(interface
, new_vpn_config
);
482 CFRelease(new_vpn_config
);
483 CFRelease(new_app_rules
);
487 _SCErrorSet(kSCStatusFailed
);
490 _SCErrorSet(kSCStatusNoKey
);
493 _SCErrorSet(kSCStatusFailed
);
501 VPNServiceIsManagedAppVPN(VPNServiceRef service
)
503 Boolean result
= FALSE
;
504 CFStringRef mc_external_id
= SCNetworkServiceCopyExternalID(service
, CFSTR("MCVPNUUID"));
505 if (isA_CFString(mc_external_id
)) {
508 if (mc_external_id
!= NULL
) {
509 CFRelease(mc_external_id
);