]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/VPNService.c
configd-596.12.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / VPNService.c
1 /*
2 * Copyright (c) 2012, 2013 Apple Inc. All rights reserved.
3 */
4
5 #include <SystemConfiguration/SystemConfiguration.h>
6 #include <SCPrivate.h>
7
8 static Boolean
9 isA_VPNService(CFTypeRef cf)
10 {
11 if (isA_SCNetworkService(cf)) {
12 SCNetworkInterfaceRef interface = SCNetworkServiceGetInterface((SCNetworkServiceRef)cf);
13
14 return (interface != NULL &&
15 CFEqual(SCNetworkInterfaceGetInterfaceType(interface), kSCNetworkInterfaceTypeVPN));
16 }
17
18 return FALSE;
19 }
20
21
22 static CFArrayRef
23 copy_matching_services(SCPreferencesRef prefs, CFStringRef identifierDomain, CFStringRef identifier)
24 {
25 CFMutableArrayRef results = NULL;
26 CFArrayRef services;
27
28 services = SCNetworkServiceCopyAll(prefs);
29 if (services != NULL) {
30 CFIndex idx;
31 CFIndex service_count = CFArrayGetCount(services);
32
33 for (idx = 0; idx < service_count; idx++) {
34 SCNetworkServiceRef service = CFArrayGetValueAtIndex(services, idx);
35 Boolean matches = FALSE;
36
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);
43 }
44 } else {
45 matches = TRUE;
46 }
47 }
48
49 if (matches) {
50 if (results == NULL) {
51 results = CFArrayCreateMutable(kCFAllocatorDefault, CFArrayGetCount(services) - idx, &kCFTypeArrayCallBacks);
52 }
53 CFArrayAppendValue(results, service);
54 }
55 }
56
57 CFRelease(services);
58 }
59
60 return results;
61 }
62
63
64 static CFIndex
65 find_app_rule(CFDictionaryRef vpn_config, CFStringRef ruleIdentifier)
66 {
67 CFArrayRef app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
68 CFIndex idx;
69
70 if (isA_CFArray(app_rules)) {
71 CFIndex rule_count = CFArrayGetCount(app_rules);
72
73 for (idx = 0; idx < rule_count; idx++) {
74 CFDictionaryRef rule = CFArrayGetValueAtIndex(app_rules, idx);
75
76 if (isA_CFDictionary(rule)) {
77 CFStringRef rule_id = CFDictionaryGetValue(rule, kSCValNetVPNAppRuleIdentifier);
78
79 if (CFEqual(ruleIdentifier, rule_id)) {
80 return idx;
81 }
82 }
83 }
84 }
85
86 return -1;
87 }
88
89 static Boolean
90 validate_app_rule(CFDictionaryRef ruleSettings)
91 {
92 CFIndex account_count;
93 CFArrayRef accounts;
94 Boolean accounts_valid = FALSE;
95 CFArrayRef executables;
96 Boolean executables_valid = FALSE;
97 CFIndex executable_count;
98 CFIndex idx;
99 CFArrayRef match_domains;
100
101 if (!isA_CFDictionary(ruleSettings)) {
102 return FALSE;
103 }
104
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);
111
112 if (isA_CFDictionary(executable)) {
113 CFStringRef signingID = CFDictionaryGetValue(executable, kSCValNetVPNAppRuleExecutableSigningIdentifier);
114 CFStringRef requirement = CFDictionaryGetValue(executable, kSCValNetVPNAppRuleExecutableDesignatedRequirement);
115
116 if (!isA_CFString(signingID) || CFStringGetLength(signingID) == 0) {
117 executables_valid = FALSE;
118 break;
119 }
120
121 if (requirement != NULL) {
122 if (!isA_CFString(requirement) || CFStringGetLength(requirement) == 0) {
123 executables_valid = FALSE;
124 break;
125 }
126 #if !TARGET_OS_IPHONE
127 } else {
128 executables_valid = FALSE;
129 break;
130 #endif /* !TARGET_OS_IPHONE */
131 }
132 }
133 }
134 }
135
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;
144 break;
145 }
146 }
147 }
148
149 /* Either executables or accounts must be present */
150 if (!executables_valid && !accounts_valid) {
151 return FALSE;
152 }
153
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;
158
159 if (!isA_CFArray(match_domains)) {
160 return FALSE;
161 }
162
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)) {
167 return FALSE;
168 }
169 }
170 }
171
172 return TRUE;
173 }
174
175
176 CFArrayRef
177 VPNServiceCopyAllMatchingExternalID(SCPreferencesRef prefs, CFStringRef identifierDomain, CFStringRef identifier)
178 {
179 CFArrayRef services;
180
181 if (prefs == NULL || !isA_CFString(identifierDomain) || !isA_CFString(identifier)) {
182 _SCErrorSet(kSCStatusInvalidArgument);
183 return NULL;
184 }
185
186 services = copy_matching_services(prefs, identifierDomain, identifier);
187 if (services == NULL) {
188 _SCErrorSet(kSCStatusOK);
189 }
190
191 return services;
192 }
193
194
195 CFArrayRef
196 VPNServiceCopyAll(SCPreferencesRef prefs)
197 {
198 CFArrayRef services;
199
200 if (prefs == NULL) {
201 _SCErrorSet(kSCStatusInvalidArgument);
202 return NULL;
203 }
204
205 services = copy_matching_services(prefs, NULL, NULL);
206 if (services == NULL) {
207 _SCErrorSet(kSCStatusOK);
208 }
209
210 return services;
211 }
212
213
214 CFArrayRef
215 VPNServiceCopyAppRuleIDs(VPNServiceRef service)
216 {
217 CFMutableArrayRef results = NULL;
218 CFDictionaryRef vpn_config;
219
220 if (!isA_VPNService(service)) {
221 _SCErrorSet(kSCStatusInvalidArgument);
222 return NULL;
223 }
224
225 vpn_config = SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service));
226
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);
231 CFIndex idx;
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));
239 }
240 }
241 }
242 if (CFArrayGetCount(results) == 0) {
243 CFRelease(results);
244 results = NULL;
245 }
246 }
247 }
248
249 if (results == NULL) {
250 _SCErrorSet(kSCStatusOK);
251 }
252
253 return results;
254 }
255
256
257 Boolean
258 VPNServiceSetAppRule(VPNServiceRef service, CFStringRef ruleIdentifier, CFDictionaryRef ruleSettings)
259 {
260 CFArrayRef accounts;
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;
269
270 /* Basic parameter validation */
271
272 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
273 _SCErrorSet(kSCStatusInvalidArgument);
274 return FALSE;
275 }
276
277 if (!validate_app_rule(ruleSettings)) {
278 _SCErrorSet(kSCStatusInvalidArgument);
279 return FALSE;
280 }
281
282 executables = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleExecutableMatch);
283 match_domains = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleDNSDomainMatch);
284 accounts = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleAccountIdentifierMatch);
285
286 /* Set the new rule config, replacing any existing rule */
287
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);
292 } else {
293 new_vpn_config = CFDictionaryCreateMutable(kCFAllocatorDefault,
294 0,
295 &kCFTypeDictionaryKeyCallBacks,
296 &kCFTypeDictionaryValueCallBacks);
297 }
298
299 app_rules = CFDictionaryGetValue(new_vpn_config, kSCPropNetVPNAppRules);
300 if (isA_CFArray(app_rules)) {
301 new_app_rules = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, app_rules);
302 } else {
303 new_app_rules = CFArrayCreateMutable(kCFAllocatorDefault,
304 0,
305 &kCFTypeArrayCallBacks);
306 }
307
308 new_settings = CFDictionaryCreateMutable(kCFAllocatorDefault,
309 0,
310 &kCFTypeDictionaryKeyCallBacks,
311 &kCFTypeDictionaryValueCallBacks);
312
313 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleIdentifier, ruleIdentifier);
314 if (executables != NULL && CFArrayGetCount(executables) > 0) {
315 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleExecutableMatch, executables);
316 }
317 if (match_domains != NULL && CFArrayGetCount(match_domains) > 0) {
318 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleDNSDomainMatch, match_domains);
319 }
320 if (accounts != NULL && CFArrayGetCount(accounts) > 0) {
321 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleAccountIdentifierMatch, accounts);
322 }
323
324 if (existing_idx >= 0) {
325 CFArraySetValueAtIndex(new_app_rules, existing_idx, new_settings);
326 } else {
327 CFArrayAppendValue(new_app_rules, new_settings);
328 }
329
330 CFDictionarySetValue(new_vpn_config, kSCPropNetVPNAppRules, new_app_rules);
331
332 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service), new_vpn_config);
333
334 CFRelease(new_vpn_config);
335 CFRelease(new_app_rules);
336 CFRelease(new_settings);
337
338 return TRUE;
339 }
340
341
342 CFDictionaryRef
343 VPNServiceCopyAppRule(VPNServiceRef service, CFStringRef ruleIdentifier)
344 {
345 CFDictionaryRef vpn_config;
346
347 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
348 _SCErrorSet(kSCStatusInvalidArgument);
349 return NULL;
350 }
351
352 vpn_config = SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service));
353 if (isA_CFDictionary(vpn_config)) {
354 CFIndex idx = find_app_rule(vpn_config, ruleIdentifier);
355 if (idx >= 0) {
356 CFArrayRef app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
357 CFDictionaryRef ruleSettings = CFArrayGetValueAtIndex(app_rules, idx);
358
359 if (validate_app_rule(ruleSettings)) {
360 return (CFDictionaryRef)CFRetain(ruleSettings);
361 } else {
362 _SCErrorSet(kSCStatusFailed);
363 }
364 } else {
365 _SCErrorSet(kSCStatusNoKey);
366 }
367 } else {
368 _SCErrorSet(kSCStatusFailed);
369 }
370
371 return NULL;
372 }
373
374
375 Boolean
376 VPNServiceRemoveAppRule(VPNServiceRef service, CFStringRef ruleIdentifier)
377 {
378 CFDictionaryRef vpn_config;
379
380 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
381 _SCErrorSet(kSCStatusInvalidArgument);
382 return FALSE;
383 }
384
385 vpn_config = SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service));
386 if (isA_CFDictionary(vpn_config)) {
387 CFIndex idx = find_app_rule(vpn_config, ruleIdentifier);
388 if (idx >= 0) {
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;
394
395 new_vpn_config = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, vpn_config);
396 new_app_rules = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, current_app_rules);
397
398 CFArrayRemoveValueAtIndex(new_app_rules, idx);
399 if (CFArrayGetCount(new_app_rules) > 0) {
400 CFDictionarySetValue(new_vpn_config, kSCPropNetVPNAppRules, new_app_rules);
401 } else {
402 CFDictionaryRemoveValue(new_vpn_config, kSCPropNetVPNAppRules);
403 }
404
405 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service), new_vpn_config);
406
407 CFRelease(new_vpn_config);
408 CFRelease(new_app_rules);
409
410 return TRUE;
411 } else {
412 _SCErrorSet(kSCStatusFailed);
413 }
414 } else {
415 _SCErrorSet(kSCStatusNoKey);
416 }
417 } else {
418 _SCErrorSet(kSCStatusFailed);
419 }
420
421 return FALSE;
422 }
423