2 * Copyright (c) 2012, 2013 Apple Inc. All rights reserved.
5 #include <SystemConfiguration/SystemConfiguration.h>
9 isA_VPNService(CFTypeRef cf
)
11 if (isA_SCNetworkService(cf
)) {
12 SCNetworkInterfaceRef interface
= SCNetworkServiceGetInterface((SCNetworkServiceRef
)cf
);
14 return (interface
!= NULL
&&
15 CFEqual(SCNetworkInterfaceGetInterfaceType(interface
), kSCNetworkInterfaceTypeVPN
));
23 copy_matching_services(SCPreferencesRef prefs
, CFStringRef identifierDomain
, CFStringRef identifier
)
25 CFMutableArrayRef results
= NULL
;
28 services
= SCNetworkServiceCopyAll(prefs
);
29 if (services
!= NULL
) {
31 CFIndex service_count
= CFArrayGetCount(services
);
33 for (idx
= 0; idx
< service_count
; idx
++) {
34 SCNetworkServiceRef service
= CFArrayGetValueAtIndex(services
, idx
);
35 Boolean matches
= FALSE
;
37 if (isA_VPNService(service
)) {
38 if (isA_CFString(identifierDomain
) && isA_CFString(identifier
)) {
39 CFStringRef ex_identifier
= SCNetworkServiceCopyExternalID(service
, identifierDomain
);
40 if (ex_identifier
!= NULL
) {
41 matches
= CFEqual(ex_identifier
, identifier
);
42 CFRelease(ex_identifier
);
50 if (results
== NULL
) {
51 results
= CFArrayCreateMutable(kCFAllocatorDefault
, CFArrayGetCount(services
) - idx
, &kCFTypeArrayCallBacks
);
53 CFArrayAppendValue(results
, service
);
65 find_app_rule(CFDictionaryRef vpn_config
, CFStringRef ruleIdentifier
)
67 CFArrayRef app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
70 if (isA_CFArray(app_rules
)) {
71 CFIndex rule_count
= CFArrayGetCount(app_rules
);
73 for (idx
= 0; idx
< rule_count
; idx
++) {
74 CFDictionaryRef rule
= CFArrayGetValueAtIndex(app_rules
, idx
);
76 if (isA_CFDictionary(rule
)) {
77 CFStringRef rule_id
= CFDictionaryGetValue(rule
, kSCValNetVPNAppRuleIdentifier
);
79 if (CFEqual(ruleIdentifier
, rule_id
)) {
90 validate_app_rule(CFDictionaryRef ruleSettings
)
92 CFIndex account_count
;
94 Boolean accounts_valid
= FALSE
;
95 CFArrayRef executables
;
96 Boolean executables_valid
= FALSE
;
97 CFIndex executable_count
;
99 CFArrayRef match_domains
;
101 if (!isA_CFDictionary(ruleSettings
)) {
105 /* Validate the executable array. It needs to have at least one value. */
106 executables
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleExecutableMatch
);
107 if (isA_CFArray(executables
) && (executable_count
= CFArrayGetCount(executables
)) > 0) {
108 executables_valid
= TRUE
;
109 for (idx
= 0; idx
< executable_count
; idx
++) {
110 CFDictionaryRef executable
= CFArrayGetValueAtIndex(executables
, idx
);
112 if (isA_CFDictionary(executable
)) {
113 CFStringRef signingID
= CFDictionaryGetValue(executable
, kSCValNetVPNAppRuleExecutableSigningIdentifier
);
114 CFStringRef requirement
= CFDictionaryGetValue(executable
, kSCValNetVPNAppRuleExecutableDesignatedRequirement
);
116 if (!isA_CFString(signingID
) || CFStringGetLength(signingID
) == 0) {
117 executables_valid
= FALSE
;
121 if (requirement
!= NULL
) {
122 if (!isA_CFString(requirement
) || CFStringGetLength(requirement
) == 0) {
123 executables_valid
= FALSE
;
126 #if !TARGET_OS_IPHONE
128 executables_valid
= FALSE
;
130 #endif /* !TARGET_OS_IPHONE */
136 /* Validate the accounts array. It needs to have at least one value. */
137 accounts
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleAccountIdentifierMatch
);
138 if (isA_CFArray(accounts
) && (account_count
= CFArrayGetCount(accounts
)) > 0) {
139 accounts_valid
= TRUE
;
140 for (idx
= 0; idx
< account_count
; idx
++) {
141 CFStringRef account
= CFArrayGetValueAtIndex(accounts
, idx
);
142 if (!isA_CFString(account
)) {
143 accounts_valid
= FALSE
;
149 /* Either executables or accounts must be present */
150 if (!executables_valid
&& !accounts_valid
) {
154 /* Validate the domains array. It's optional, so just make sure that it contains only strings if it's present. */
155 match_domains
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleDNSDomainMatch
);
156 if (match_domains
!= NULL
) {
157 CFIndex match_domain_count
;
159 if (!isA_CFArray(match_domains
)) {
163 match_domain_count
= CFArrayGetCount(match_domains
);
164 for (idx
= 0; idx
< match_domain_count
; idx
++) {
165 CFStringRef domain
= CFArrayGetValueAtIndex(match_domains
, idx
);
166 if (!isA_CFString(domain
)) {
177 VPNServiceCopyAllMatchingExternalID(SCPreferencesRef prefs
, CFStringRef identifierDomain
, CFStringRef identifier
)
181 if (prefs
== NULL
|| !isA_CFString(identifierDomain
) || !isA_CFString(identifier
)) {
182 _SCErrorSet(kSCStatusInvalidArgument
);
186 services
= copy_matching_services(prefs
, identifierDomain
, identifier
);
187 if (services
== NULL
) {
188 _SCErrorSet(kSCStatusOK
);
196 VPNServiceCopyAll(SCPreferencesRef prefs
)
201 _SCErrorSet(kSCStatusInvalidArgument
);
205 services
= copy_matching_services(prefs
, NULL
, NULL
);
206 if (services
== NULL
) {
207 _SCErrorSet(kSCStatusOK
);
215 VPNServiceCopyAppRuleIDs(VPNServiceRef service
)
217 CFMutableArrayRef results
= NULL
;
218 CFDictionaryRef vpn_config
;
220 if (!isA_VPNService(service
)) {
221 _SCErrorSet(kSCStatusInvalidArgument
);
225 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
227 if (isA_CFDictionary(vpn_config
)) {
228 CFArrayRef app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
229 if (isA_CFArray(app_rules
)) {
230 CFIndex app_rule_count
= CFArrayGetCount(app_rules
);
232 results
= CFArrayCreateMutable(kCFAllocatorDefault
, app_rule_count
, &kCFTypeArrayCallBacks
);
233 for (idx
= 0; idx
< app_rule_count
; idx
++) {
234 CFDictionaryRef rule
= CFArrayGetValueAtIndex(app_rules
, idx
);
235 if (isA_CFDictionary(rule
)) {
236 CFStringRef rule_ID
= CFDictionaryGetValue(rule
, kSCValNetVPNAppRuleIdentifier
);
237 if (isA_CFString(rule_ID
)) {
238 CFArrayAppendValue(results
, CFDictionaryGetValue(rule
, kSCValNetVPNAppRuleIdentifier
));
242 if (CFArrayGetCount(results
) == 0) {
249 if (results
== NULL
) {
250 _SCErrorSet(kSCStatusOK
);
258 VPNServiceSetAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
, CFDictionaryRef ruleSettings
)
261 CFArrayRef app_rules
;
262 CFArrayRef executables
;
263 CFIndex existing_idx
= -1;
264 CFArrayRef match_domains
;
265 CFMutableArrayRef new_app_rules
;
266 CFMutableDictionaryRef new_settings
;
267 CFMutableDictionaryRef new_vpn_config
;
268 CFDictionaryRef vpn_config
;
270 /* Basic parameter validation */
272 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
273 _SCErrorSet(kSCStatusInvalidArgument
);
277 if (!validate_app_rule(ruleSettings
)) {
278 _SCErrorSet(kSCStatusInvalidArgument
);
282 executables
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleExecutableMatch
);
283 match_domains
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleDNSDomainMatch
);
284 accounts
= CFDictionaryGetValue(ruleSettings
, kSCValNetVPNAppRuleAccountIdentifierMatch
);
286 /* Set the new rule config, replacing any existing rule */
288 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
289 if (isA_CFDictionary(vpn_config
)) {
290 existing_idx
= find_app_rule(vpn_config
, ruleIdentifier
);
291 new_vpn_config
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, vpn_config
);
293 new_vpn_config
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
295 &kCFTypeDictionaryKeyCallBacks
,
296 &kCFTypeDictionaryValueCallBacks
);
299 app_rules
= CFDictionaryGetValue(new_vpn_config
, kSCPropNetVPNAppRules
);
300 if (isA_CFArray(app_rules
)) {
301 new_app_rules
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, app_rules
);
303 new_app_rules
= CFArrayCreateMutable(kCFAllocatorDefault
,
305 &kCFTypeArrayCallBacks
);
308 new_settings
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
310 &kCFTypeDictionaryKeyCallBacks
,
311 &kCFTypeDictionaryValueCallBacks
);
313 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleIdentifier
, ruleIdentifier
);
314 if (executables
!= NULL
&& CFArrayGetCount(executables
) > 0) {
315 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleExecutableMatch
, executables
);
317 if (match_domains
!= NULL
&& CFArrayGetCount(match_domains
) > 0) {
318 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleDNSDomainMatch
, match_domains
);
320 if (accounts
!= NULL
&& CFArrayGetCount(accounts
) > 0) {
321 CFDictionarySetValue(new_settings
, kSCValNetVPNAppRuleAccountIdentifierMatch
, accounts
);
324 if (existing_idx
>= 0) {
325 CFArraySetValueAtIndex(new_app_rules
, existing_idx
, new_settings
);
327 CFArrayAppendValue(new_app_rules
, new_settings
);
330 CFDictionarySetValue(new_vpn_config
, kSCPropNetVPNAppRules
, new_app_rules
);
332 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service
), new_vpn_config
);
334 CFRelease(new_vpn_config
);
335 CFRelease(new_app_rules
);
336 CFRelease(new_settings
);
343 VPNServiceCopyAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
)
345 CFDictionaryRef vpn_config
;
347 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
348 _SCErrorSet(kSCStatusInvalidArgument
);
352 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
353 if (isA_CFDictionary(vpn_config
)) {
354 CFIndex idx
= find_app_rule(vpn_config
, ruleIdentifier
);
356 CFArrayRef app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
357 CFDictionaryRef ruleSettings
= CFArrayGetValueAtIndex(app_rules
, idx
);
359 if (validate_app_rule(ruleSettings
)) {
360 return (CFDictionaryRef
)CFRetain(ruleSettings
);
362 _SCErrorSet(kSCStatusFailed
);
365 _SCErrorSet(kSCStatusNoKey
);
368 _SCErrorSet(kSCStatusFailed
);
376 VPNServiceRemoveAppRule(VPNServiceRef service
, CFStringRef ruleIdentifier
)
378 CFDictionaryRef vpn_config
;
380 if (!isA_VPNService(service
) || !isA_CFString(ruleIdentifier
)) {
381 _SCErrorSet(kSCStatusInvalidArgument
);
385 vpn_config
= SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service
));
386 if (isA_CFDictionary(vpn_config
)) {
387 CFIndex idx
= find_app_rule(vpn_config
, ruleIdentifier
);
389 CFArrayRef current_app_rules
;
390 current_app_rules
= CFDictionaryGetValue(vpn_config
, kSCPropNetVPNAppRules
);
391 if (isA_CFArray(current_app_rules
)) {
392 CFMutableDictionaryRef new_vpn_config
;
393 CFMutableArrayRef new_app_rules
;
395 new_vpn_config
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, vpn_config
);
396 new_app_rules
= CFArrayCreateMutableCopy(kCFAllocatorDefault
, 0, current_app_rules
);
398 CFArrayRemoveValueAtIndex(new_app_rules
, idx
);
399 if (CFArrayGetCount(new_app_rules
) > 0) {
400 CFDictionarySetValue(new_vpn_config
, kSCPropNetVPNAppRules
, new_app_rules
);
402 CFDictionaryRemoveValue(new_vpn_config
, kSCPropNetVPNAppRules
);
405 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service
), new_vpn_config
);
407 CFRelease(new_vpn_config
);
408 CFRelease(new_app_rules
);
412 _SCErrorSet(kSCStatusFailed
);
415 _SCErrorSet(kSCStatusNoKey
);
418 _SCErrorSet(kSCStatusFailed
);