2 * Copyright (c) 2012, 2013 Apple Inc. All rights reserved.
5 #include <SystemConfiguration/SystemConfiguration.h>
6 #include <SystemConfiguration/SCPrivate.h>
7 #include "dy_framework.h"
9 static CFStringRef g_apple_app_prefix
= CFSTR("com.apple.");
12 CFStringRef signing_id
;
13 Boolean domains_required
;
14 } g_apple_app_exceptions
[] = {
16 { CFSTR("com.apple.mobilesafari"), TRUE
},
17 { CFSTR("com.apple.webapp"), TRUE
},
19 { CFSTR("com.apple.WebKit.NetworkProcess"), TRUE
},
25 isA_VPNService(CFTypeRef cf
)
27 if (isA_SCNetworkService(cf
)) {
28 SCNetworkInterfaceRef interface
= SCNetworkServiceGetInterface((SCNetworkServiceRef
)cf
);
30 return (interface
!= NULL
&&
31 CFEqual(SCNetworkInterfaceGetInterfaceType(interface
), kSCNetworkInterfaceTypeVPN
));
39 copy_matching_services(SCPreferencesRef prefs
, CFStringRef identifierDomain
, CFStringRef identifier
)
41 CFMutableArrayRef results
= NULL
;
44 services
= SCNetworkServiceCopyAll(prefs
);
45 if (services
!= NULL
) {
47 CFIndex service_count
= CFArrayGetCount(services
);
49 for (idx
= 0; idx
< service_count
; idx
++) {
50 SCNetworkServiceRef service
= CFArrayGetValueAtIndex(services
, idx
);
51 Boolean matches
= FALSE
;
53 if (isA_VPNService(service
)) {
54 if (isA_CFString(identifierDomain
) && isA_CFString(identifier
)) {
55 CFStringRef ex_identifier
= SCNetworkServiceCopyExternalID(service
, identifierDomain
);
56 if (ex_identifier
!= NULL
) {
57 matches
= CFEqual(ex_identifier
, identifier
);
58 CFRelease(ex_identifier
);
66 if (results
== NULL
) {
67 results
= CFArrayCreateMutable(kCFAllocatorDefault
, CFArrayGetCount(services
) - idx
, &kCFTypeArrayCallBacks
);
69 CFArrayAppendValue(results
, service
);
81 find_app_rule(CFDictionaryRef vpn_config
, CFStringRef ruleIdentifier
)
83 CFArrayRef app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
86 if (isA_CFArray(app_rules
)) {
87 CFIndex rule_count
= CFArrayGetCount(app_rules
);
89 for (idx
= 0; idx
< rule_count
; idx
++) {
90 CFDictionaryRef rule
= CFArrayGetValueAtIndex(app_rules
, idx
);
92 if (isA_CFDictionary(rule
)) {
93 CFStringRef rule_id
= CFDictionaryGetValue(rule
, kSCValNetVPNAppRuleIdentifier
);
95 if (CFEqual(ruleIdentifier
, rule_id
)) {
106 validate_app_rule(CFDictionaryRef ruleSettings
, Boolean check_for_apple_apps
)
108 CFIndex account_count
= 0;
110 CFIndex exception_idx
= -1;
111 CFArrayRef executables
;
112 CFIndex executable_count
= 0;
113 Boolean found_exception
= FALSE
;
115 CFArrayRef match_domains
;
116 CFIndex match_domain_count
= 0;
118 if (!isA_CFDictionary(ruleSettings
)) {
122 /* Validate the executable array. It needs to have at least one value. */
123 executables
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleExecutableMatch
);
124 if (isA_CFArray(executables
) && (executable_count
= CFArrayGetCount(executables
)) > 0) {
125 for (idx
= 0; idx
< executable_count
; idx
++) {
126 CFDictionaryRef executable
= CFArrayGetValueAtIndex(executables
, idx
);
128 if (isA_CFDictionary(executable
)) {
129 CFStringRef signingID
= CFDictionaryGetValue(executable
, kSCValNetVPNAppRuleExecutableSigningIdentifier
);
130 CFStringRef requirement
= CFDictionaryGetValue(executable
, kSCValNetVPNAppRuleExecutableDesignatedRequirement
);
132 if (!isA_CFString(signingID
) || CFStringGetLength(signingID
) == 0) {
136 if (check_for_apple_apps
&& CFStringHasPrefix(signingID
, g_apple_app_prefix
)) {
137 for (exception_idx
= 0;
138 exception_idx
< sizeof(g_apple_app_exceptions
) / sizeof(g_apple_app_exceptions
[0]);
141 if (CFStringCompare(signingID
, g_apple_app_exceptions
[exception_idx
].signing_id
, 0) == 0) {
142 found_exception
= TRUE
;
147 if (!found_exception
) {
148 Boolean can_set_apple_app_rules
= FALSE
;
149 SecTaskRef current_task
= SecTaskCreateFromSelf(kCFAllocatorDefault
);
150 if (current_task
!= NULL
) {
151 CFBooleanRef entitlement
=
152 SecTaskCopyValueForEntitlement(current_task
,
153 CFSTR("com.apple.private.app-vpn-config"),
155 can_set_apple_app_rules
= (isA_CFBoolean(entitlement
) && CFBooleanGetValue(entitlement
));
156 if (entitlement
!= NULL
) {
157 CFRelease(entitlement
);
159 CFRelease(current_task
);
161 if (!can_set_apple_app_rules
) {
167 if (requirement
!= NULL
) {
168 if (!isA_CFString(requirement
) || CFStringGetLength(requirement
) == 0) {
171 #if !TARGET_OS_IPHONE
174 #endif /* !TARGET_OS_IPHONE */
180 /* Validate the accounts array. It needs to have at least one value. */
181 accounts
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleAccountIdentifierMatch
);
182 if (isA_CFArray(accounts
) && (account_count
= CFArrayGetCount(accounts
)) > 0) {
183 for (idx
= 0; idx
< account_count
; idx
++) {
184 CFStringRef account
= CFArrayGetValueAtIndex(accounts
, idx
);
185 if (!isA_CFString(account
)) {
191 /* Either executables or accounts must be present */
192 if (executable_count
== 0 && account_count
== 0) {
196 /* Validate the domains array. It's optional, so just make sure that it contains only strings if it's present. */
197 match_domains
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleDNSDomainMatch
);
198 if (match_domains
!= NULL
) {
199 if (!isA_CFArray(match_domains
)) {
203 match_domain_count
= CFArrayGetCount(match_domains
);
204 for (idx
= 0; idx
< match_domain_count
; idx
++) {
205 CFStringRef domain
= CFArrayGetValueAtIndex(match_domains
, idx
);
206 if (!isA_CFString(domain
)) {
212 /* Require at least one match domain for some Apple apps (like Safari) */
213 if (match_domain_count
== 0 &&
215 exception_idx
>= 0 &&
216 g_apple_app_exceptions
[exception_idx
].domains_required
)
226 VPNServiceCopyAllMatchingExternalID(SCPreferencesRef prefs
, CFStringRef identifierDomain
, CFStringRef identifier
)
230 if (prefs
== NULL
|| !isA_CFString(identifierDomain
) || !isA_CFString(identifier
)) {
231 _SCErrorSet(kSCStatusInvalidArgument
);
235 services
= copy_matching_services(prefs
, identifierDomain
, identifier
);
236 if (services
== NULL
) {
237 _SCErrorSet(kSCStatusOK
);
245 VPNServiceCopyAll(SCPreferencesRef prefs
)
250 _SCErrorSet(kSCStatusInvalidArgument
);
254 services
= copy_matching_services(prefs
, NULL
, NULL
);
255 if (services
== NULL
) {
256 _SCErrorSet(kSCStatusOK
);
264 VPNServiceCopyAppRuleIDs(VPNServiceRef service
)
266 CFMutableArrayRef results
= NULL
;
267 CFDictionaryRef vpn_config
;
269 if (!isA_VPNService(service
)) {
270 _SCErrorSet(kSCStatusInvalidArgument
);
274 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
276 if (isA_CFDictionary(vpn_config
)) {
277 CFArrayRef app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
278 if (isA_CFArray(app_rules
)) {
279 CFIndex app_rule_count
= CFArrayGetCount(app_rules
);
281 results
= CFArrayCreateMutable(kCFAllocatorDefault
, app_rule_count
, &kCFTypeArrayCallBacks
);
282 for (idx
= 0; idx
< app_rule_count
; idx
++) {
283 CFDictionaryRef rule
= CFArrayGetValueAtIndex(app_rules
, idx
);
284 if (isA_CFDictionary(rule
)) {
285 CFStringRef rule_ID
= CFDictionaryGetValue(rule
, kSCValNetVPNAppRuleIdentifier
);
286 if (isA_CFString(rule_ID
)) {
287 CFArrayAppendValue(results
, CFDictionaryGetValue(rule
, kSCValNetVPNAppRuleIdentifier
));
291 if (CFArrayGetCount(results
) == 0) {
298 if (results
== NULL
) {
299 _SCErrorSet(kSCStatusOK
);
307 VPNServiceSetAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
, CFDictionaryRef ruleSettings
)
310 CFArrayRef app_rules
;
311 CFArrayRef executables
;
312 CFIndex existing_idx
= -1;
313 CFArrayRef match_domains
;
314 CFMutableArrayRef new_app_rules
;
315 CFMutableDictionaryRef new_settings
;
316 CFMutableDictionaryRef new_vpn_config
;
317 CFDictionaryRef vpn_config
;
319 /* Basic parameter validation */
321 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
322 _SCErrorSet(kSCStatusInvalidArgument
);
326 if (!validate_app_rule(ruleSettings
, TRUE
)) {
327 _SCErrorSet(kSCStatusInvalidArgument
);
331 executables
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleExecutableMatch
);
332 match_domains
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleDNSDomainMatch
);
333 accounts
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleAccountIdentifierMatch
);
335 /* Set the new rule config, replacing any existing rule */
337 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
338 if (isA_CFDictionary(vpn_config
)) {
339 existing_idx
= find_app_rule(vpn_config
, ruleIdentifier
);
340 new_vpn_config
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, vpn_config
);
342 new_vpn_config
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
344 &kCFTypeDictionaryKeyCallBacks
,
345 &kCFTypeDictionaryValueCallBacks
);
348 app_rules
= CFDictionaryGetValue(new_vpn_config
, kSCPropNetVPNAppRules
);
349 if (isA_CFArray(app_rules
)) {
350 new_app_rules
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, app_rules
);
352 new_app_rules
= CFArrayCreateMutable(kCFAllocatorDefault
,
354 &kCFTypeArrayCallBacks
);
357 new_settings
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
359 &kCFTypeDictionaryKeyCallBacks
,
360 &kCFTypeDictionaryValueCallBacks
);
362 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleIdentifier
, ruleIdentifier
);
363 if (executables
!= NULL
&& CFArrayGetCount(executables
) > 0) {
364 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleExecutableMatch
, executables
);
366 if (match_domains
!= NULL
&& CFArrayGetCount(match_domains
) > 0) {
367 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleDNSDomainMatch
, match_domains
);
369 if (accounts
!= NULL
&& CFArrayGetCount(accounts
) > 0) {
370 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleAccountIdentifierMatch
, accounts
);
373 if (existing_idx
>= 0) {
374 CFArraySetValueAtIndex(new_app_rules
, existing_idx
, new_settings
);
376 CFArrayAppendValue(new_app_rules
, new_settings
);
379 CFDictionarySetValue(new_vpn_config
, kSCPropNetVPNAppRules
, new_app_rules
);
381 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service
), new_vpn_config
);
383 CFRelease(new_vpn_config
);
384 CFRelease(new_app_rules
);
385 CFRelease(new_settings
);
392 VPNServiceCopyAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
)
394 CFDictionaryRef vpn_config
;
396 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
397 _SCErrorSet(kSCStatusInvalidArgument
);
401 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
402 if (isA_CFDictionary(vpn_config
)) {
403 CFIndex idx
= find_app_rule(vpn_config
, ruleIdentifier
);
405 CFArrayRef app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
406 CFDictionaryRef ruleSettings
= CFArrayGetValueAtIndex(app_rules
, idx
);
408 if (validate_app_rule(ruleSettings
, FALSE
)) {
409 return (CFDictionaryRef
)CFRetain(ruleSettings
);
411 _SCErrorSet(kSCStatusFailed
);
414 _SCErrorSet(kSCStatusNoKey
);
417 _SCErrorSet(kSCStatusFailed
);
425 VPNServiceRemoveAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
)
427 CFDictionaryRef vpn_config
;
429 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
430 _SCErrorSet(kSCStatusInvalidArgument
);
434 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
435 if (isA_CFDictionary(vpn_config
)) {
436 CFIndex idx
= find_app_rule(vpn_config
, ruleIdentifier
);
438 CFArrayRef current_app_rules
;
439 current_app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
440 if (isA_CFArray(current_app_rules
)) {
441 CFMutableDictionaryRef new_vpn_config
;
442 CFMutableArrayRef new_app_rules
;
444 new_vpn_config
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, vpn_config
);
445 new_app_rules
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, current_app_rules
);
447 CFArrayRemoveValueAtIndex(new_app_rules
, idx
);
448 if (CFArrayGetCount(new_app_rules
) > 0) {
449 CFDictionarySetValue(new_vpn_config
, kSCPropNetVPNAppRules
, new_app_rules
);
451 CFDictionaryRemoveValue(new_vpn_config
, kSCPropNetVPNAppRules
);
454 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service
), new_vpn_config
);
456 CFRelease(new_vpn_config
);
457 CFRelease(new_app_rules
);
461 _SCErrorSet(kSCStatusFailed
);
464 _SCErrorSet(kSCStatusNoKey
);
467 _SCErrorSet(kSCStatusFailed
);
475 VPNServiceIsManagedAppVPN(VPNServiceRef service
)
477 Boolean result
= FALSE
;
478 CFStringRef mc_external_id
= SCNetworkServiceCopyExternalID(service
, CFSTR("MCVPNUUID"));
479 if (isA_CFString(mc_external_id
)) {
482 if (mc_external_id
!= NULL
) {
483 CFRelease(mc_external_id
);