2 * Copyright (c) 2012, 2013, 2016 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
< 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 CFMutableArrayRef results
= NULL
;
266 CFDictionaryRef vpn_config
;
268 if (!isA_VPNService(service
)) {
269 _SCErrorSet(kSCStatusInvalidArgument
);
273 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
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
);
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
));
290 if (CFArrayGetCount(results
) == 0) {
297 if (results
== NULL
) {
298 _SCErrorSet(kSCStatusOK
);
306 VPNServiceSetAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
, CFDictionaryRef ruleSettings
)
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
;
318 /* Basic parameter validation */
320 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
321 _SCErrorSet(kSCStatusInvalidArgument
);
325 if (!validate_app_rule(ruleSettings
, TRUE
)) {
326 _SCErrorSet(kSCStatusInvalidArgument
);
330 executables
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleExecutableMatch
);
331 match_domains
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleDNSDomainMatch
);
332 accounts
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleAccountIdentifierMatch
);
334 /* Set the new rule config, replacing any existing rule */
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
);
341 new_vpn_config
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
343 &kCFTypeDictionaryKeyCallBacks
,
344 &kCFTypeDictionaryValueCallBacks
);
347 app_rules
= CFDictionaryGetValue(new_vpn_config
, kSCPropNetVPNAppRules
);
348 if (isA_CFArray(app_rules
)) {
349 new_app_rules
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, app_rules
);
351 new_app_rules
= CFArrayCreateMutable(kCFAllocatorDefault
,
353 &kCFTypeArrayCallBacks
);
356 new_settings
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
358 &kCFTypeDictionaryKeyCallBacks
,
359 &kCFTypeDictionaryValueCallBacks
);
361 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleIdentifier
, ruleIdentifier
);
362 if (executables
!= NULL
&& CFArrayGetCount(executables
) > 0) {
363 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleExecutableMatch
, executables
);
365 if (match_domains
!= NULL
&& CFArrayGetCount(match_domains
) > 0) {
366 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleDNSDomainMatch
, match_domains
);
368 if (accounts
!= NULL
&& CFArrayGetCount(accounts
) > 0) {
369 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleAccountIdentifierMatch
, accounts
);
372 if (existing_idx
>= 0) {
373 CFArraySetValueAtIndex(new_app_rules
, existing_idx
, new_settings
);
375 CFArrayAppendValue(new_app_rules
, new_settings
);
378 CFDictionarySetValue(new_vpn_config
, kSCPropNetVPNAppRules
, new_app_rules
);
380 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service
), new_vpn_config
);
382 CFRelease(new_vpn_config
);
383 CFRelease(new_app_rules
);
384 CFRelease(new_settings
);
391 VPNServiceCopyAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
)
393 CFDictionaryRef vpn_config
;
395 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
396 _SCErrorSet(kSCStatusInvalidArgument
);
400 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
401 if (isA_CFDictionary(vpn_config
)) {
402 CFIndex idx
= find_app_rule(vpn_config
, ruleIdentifier
);
404 CFArrayRef app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
405 CFDictionaryRef ruleSettings
= CFArrayGetValueAtIndex(app_rules
, idx
);
407 if (validate_app_rule(ruleSettings
, FALSE
)) {
408 return (CFDictionaryRef
)CFRetain(ruleSettings
);
410 _SCErrorSet(kSCStatusFailed
);
413 _SCErrorSet(kSCStatusNoKey
);
416 _SCErrorSet(kSCStatusFailed
);
424 VPNServiceRemoveAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
)
426 CFDictionaryRef vpn_config
;
428 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
429 _SCErrorSet(kSCStatusInvalidArgument
);
433 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
434 if (isA_CFDictionary(vpn_config
)) {
435 CFIndex idx
= find_app_rule(vpn_config
, ruleIdentifier
);
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
;
443 new_vpn_config
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, vpn_config
);
444 new_app_rules
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, current_app_rules
);
446 CFArrayRemoveValueAtIndex(new_app_rules
, idx
);
447 if (CFArrayGetCount(new_app_rules
) > 0) {
448 CFDictionarySetValue(new_vpn_config
, kSCPropNetVPNAppRules
, new_app_rules
);
450 CFDictionaryRemoveValue(new_vpn_config
, kSCPropNetVPNAppRules
);
453 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service
), new_vpn_config
);
455 CFRelease(new_vpn_config
);
456 CFRelease(new_app_rules
);
460 _SCErrorSet(kSCStatusFailed
);
463 _SCErrorSet(kSCStatusNoKey
);
466 _SCErrorSet(kSCStatusFailed
);
474 VPNServiceIsManagedAppVPN(VPNServiceRef service
)
476 Boolean result
= FALSE
;
477 CFStringRef mc_external_id
= SCNetworkServiceCopyExternalID(service
, CFSTR("MCVPNUUID"));
478 if (isA_CFString(mc_external_id
)) {
481 if (mc_external_id
!= NULL
) {
482 CFRelease(mc_external_id
);