]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/VPNService.c
configd-802.40.13.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 <SystemConfiguration/SCPrivate.h>
7 #include "dy_framework.h"
8
9 static CFStringRef g_apple_app_prefix = CFSTR("com.apple.");
10
11 static struct {
12 CFStringRef signing_id;
13 Boolean domains_required;
14 } g_apple_app_exceptions[] = {
15 #if TARGET_OS_IPHONE
16 { CFSTR("com.apple.mobilesafari"), TRUE },
17 { CFSTR("com.apple.webapp"), TRUE },
18 #else
19 { CFSTR("com.apple.WebKit.NetworkProcess"), TRUE },
20 #endif
21 };
22
23
24 static Boolean
25 isA_VPNService(CFTypeRef cf)
26 {
27 if (isA_SCNetworkService(cf)) {
28 SCNetworkInterfaceRef interface = SCNetworkServiceGetInterface((SCNetworkServiceRef)cf);
29
30 return (interface != NULL &&
31 CFEqual(SCNetworkInterfaceGetInterfaceType(interface), kSCNetworkInterfaceTypeVPN));
32 }
33
34 return FALSE;
35 }
36
37
38 static CFArrayRef
39 copy_matching_services(SCPreferencesRef prefs, CFStringRef identifierDomain, CFStringRef identifier)
40 {
41 CFMutableArrayRef results = NULL;
42 CFArrayRef services;
43
44 services = SCNetworkServiceCopyAll(prefs);
45 if (services != NULL) {
46 CFIndex idx;
47 CFIndex service_count = CFArrayGetCount(services);
48
49 for (idx = 0; idx < service_count; idx++) {
50 SCNetworkServiceRef service = CFArrayGetValueAtIndex(services, idx);
51 Boolean matches = FALSE;
52
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);
59 }
60 } else {
61 matches = TRUE;
62 }
63 }
64
65 if (matches) {
66 if (results == NULL) {
67 results = CFArrayCreateMutable(kCFAllocatorDefault, CFArrayGetCount(services) - idx, &kCFTypeArrayCallBacks);
68 }
69 CFArrayAppendValue(results, service);
70 }
71 }
72
73 CFRelease(services);
74 }
75
76 return results;
77 }
78
79
80 static CFIndex
81 find_app_rule(CFDictionaryRef vpn_config, CFStringRef ruleIdentifier)
82 {
83 CFArrayRef app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
84 CFIndex idx;
85
86 if (isA_CFArray(app_rules)) {
87 CFIndex rule_count = CFArrayGetCount(app_rules);
88
89 for (idx = 0; idx < rule_count; idx++) {
90 CFDictionaryRef rule = CFArrayGetValueAtIndex(app_rules, idx);
91
92 if (isA_CFDictionary(rule)) {
93 CFStringRef rule_id = CFDictionaryGetValue(rule, kSCValNetVPNAppRuleIdentifier);
94
95 if (CFEqual(ruleIdentifier, rule_id)) {
96 return idx;
97 }
98 }
99 }
100 }
101
102 return -1;
103 }
104
105 static Boolean
106 validate_app_rule(CFDictionaryRef ruleSettings, Boolean check_for_apple_apps)
107 {
108 CFIndex account_count = 0;
109 CFArrayRef accounts;
110 CFIndex exception_idx = -1;
111 CFArrayRef executables;
112 CFIndex executable_count = 0;
113 Boolean found_exception = FALSE;
114 CFIndex idx;
115 CFArrayRef match_domains;
116 CFIndex match_domain_count = 0;
117
118 if (!isA_CFDictionary(ruleSettings)) {
119 return FALSE;
120 }
121
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);
127
128 if (isA_CFDictionary(executable)) {
129 CFStringRef signingID = CFDictionaryGetValue(executable, kSCValNetVPNAppRuleExecutableSigningIdentifier);
130 CFStringRef requirement = CFDictionaryGetValue(executable, kSCValNetVPNAppRuleExecutableDesignatedRequirement);
131
132 if (!isA_CFString(signingID) || CFStringGetLength(signingID) == 0) {
133 return FALSE;
134 }
135
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]);
139 exception_idx++)
140 {
141 if (CFStringCompare(signingID, g_apple_app_exceptions[exception_idx].signing_id, 0) == 0) {
142 found_exception = TRUE;
143 break;
144 }
145 }
146
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"),
154 NULL);
155 can_set_apple_app_rules = (isA_CFBoolean(entitlement) && CFBooleanGetValue(entitlement));
156 if (entitlement != NULL) {
157 CFRelease(entitlement);
158 }
159 CFRelease(current_task);
160 }
161 if (!can_set_apple_app_rules) {
162 return FALSE;
163 }
164 }
165 }
166
167 if (requirement != NULL) {
168 if (!isA_CFString(requirement) || CFStringGetLength(requirement) == 0) {
169 return FALSE;
170 }
171 #if !TARGET_OS_IPHONE
172 } else {
173 return FALSE;
174 #endif /* !TARGET_OS_IPHONE */
175 }
176 }
177 }
178 }
179
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)) {
186 return FALSE;
187 }
188 }
189 }
190
191 /* Either executables or accounts must be present */
192 if (executable_count == 0 && account_count == 0) {
193 return FALSE;
194 }
195
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)) {
200 return FALSE;
201 }
202
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)) {
207 return FALSE;
208 }
209 }
210 }
211
212 /* Require at least one match domain for some Apple apps (like Safari) */
213 if (match_domain_count == 0 &&
214 found_exception &&
215 exception_idx >= 0 &&
216 g_apple_app_exceptions[exception_idx].domains_required)
217 {
218 return FALSE;
219 }
220
221 return TRUE;
222 }
223
224
225 CFArrayRef
226 VPNServiceCopyAllMatchingExternalID(SCPreferencesRef prefs, CFStringRef identifierDomain, CFStringRef identifier)
227 {
228 CFArrayRef services;
229
230 if (prefs == NULL || !isA_CFString(identifierDomain) || !isA_CFString(identifier)) {
231 _SCErrorSet(kSCStatusInvalidArgument);
232 return NULL;
233 }
234
235 services = copy_matching_services(prefs, identifierDomain, identifier);
236 if (services == NULL) {
237 _SCErrorSet(kSCStatusOK);
238 }
239
240 return services;
241 }
242
243
244 CFArrayRef
245 VPNServiceCopyAll(SCPreferencesRef prefs)
246 {
247 CFArrayRef services;
248
249 if (prefs == NULL) {
250 _SCErrorSet(kSCStatusInvalidArgument);
251 return NULL;
252 }
253
254 services = copy_matching_services(prefs, NULL, NULL);
255 if (services == NULL) {
256 _SCErrorSet(kSCStatusOK);
257 }
258
259 return services;
260 }
261
262
263 CFArrayRef
264 VPNServiceCopyAppRuleIDs(VPNServiceRef service)
265 {
266 CFMutableArrayRef results = NULL;
267 CFDictionaryRef vpn_config;
268
269 if (!isA_VPNService(service)) {
270 _SCErrorSet(kSCStatusInvalidArgument);
271 return NULL;
272 }
273
274 vpn_config = SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service));
275
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);
280 CFIndex idx;
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));
288 }
289 }
290 }
291 if (CFArrayGetCount(results) == 0) {
292 CFRelease(results);
293 results = NULL;
294 }
295 }
296 }
297
298 if (results == NULL) {
299 _SCErrorSet(kSCStatusOK);
300 }
301
302 return results;
303 }
304
305
306 Boolean
307 VPNServiceSetAppRule(VPNServiceRef service, CFStringRef ruleIdentifier, CFDictionaryRef ruleSettings)
308 {
309 CFArrayRef accounts;
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;
318
319 /* Basic parameter validation */
320
321 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
322 _SCErrorSet(kSCStatusInvalidArgument);
323 return FALSE;
324 }
325
326 if (!validate_app_rule(ruleSettings, TRUE)) {
327 _SCErrorSet(kSCStatusInvalidArgument);
328 return FALSE;
329 }
330
331 executables = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleExecutableMatch);
332 match_domains = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleDNSDomainMatch);
333 accounts = CFDictionaryGetValue(ruleSettings, kSCValNetVPNAppRuleAccountIdentifierMatch);
334
335 /* Set the new rule config, replacing any existing rule */
336
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);
341 } else {
342 new_vpn_config = CFDictionaryCreateMutable(kCFAllocatorDefault,
343 0,
344 &kCFTypeDictionaryKeyCallBacks,
345 &kCFTypeDictionaryValueCallBacks);
346 }
347
348 app_rules = CFDictionaryGetValue(new_vpn_config, kSCPropNetVPNAppRules);
349 if (isA_CFArray(app_rules)) {
350 new_app_rules = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, app_rules);
351 } else {
352 new_app_rules = CFArrayCreateMutable(kCFAllocatorDefault,
353 0,
354 &kCFTypeArrayCallBacks);
355 }
356
357 new_settings = CFDictionaryCreateMutable(kCFAllocatorDefault,
358 0,
359 &kCFTypeDictionaryKeyCallBacks,
360 &kCFTypeDictionaryValueCallBacks);
361
362 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleIdentifier, ruleIdentifier);
363 if (executables != NULL && CFArrayGetCount(executables) > 0) {
364 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleExecutableMatch, executables);
365 }
366 if (match_domains != NULL && CFArrayGetCount(match_domains) > 0) {
367 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleDNSDomainMatch, match_domains);
368 }
369 if (accounts != NULL && CFArrayGetCount(accounts) > 0) {
370 CFDictionarySetValue(new_settings, kSCValNetVPNAppRuleAccountIdentifierMatch, accounts);
371 }
372
373 if (existing_idx >= 0) {
374 CFArraySetValueAtIndex(new_app_rules, existing_idx, new_settings);
375 } else {
376 CFArrayAppendValue(new_app_rules, new_settings);
377 }
378
379 CFDictionarySetValue(new_vpn_config, kSCPropNetVPNAppRules, new_app_rules);
380
381 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service), new_vpn_config);
382
383 CFRelease(new_vpn_config);
384 CFRelease(new_app_rules);
385 CFRelease(new_settings);
386
387 return TRUE;
388 }
389
390
391 CFDictionaryRef
392 VPNServiceCopyAppRule(VPNServiceRef service, CFStringRef ruleIdentifier)
393 {
394 CFDictionaryRef vpn_config;
395
396 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
397 _SCErrorSet(kSCStatusInvalidArgument);
398 return NULL;
399 }
400
401 vpn_config = SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service));
402 if (isA_CFDictionary(vpn_config)) {
403 CFIndex idx = find_app_rule(vpn_config, ruleIdentifier);
404 if (idx >= 0) {
405 CFArrayRef app_rules = CFDictionaryGetValue(vpn_config, kSCPropNetVPNAppRules);
406 CFDictionaryRef ruleSettings = CFArrayGetValueAtIndex(app_rules, idx);
407
408 if (validate_app_rule(ruleSettings, FALSE)) {
409 return (CFDictionaryRef)CFRetain(ruleSettings);
410 } else {
411 _SCErrorSet(kSCStatusFailed);
412 }
413 } else {
414 _SCErrorSet(kSCStatusNoKey);
415 }
416 } else {
417 _SCErrorSet(kSCStatusFailed);
418 }
419
420 return NULL;
421 }
422
423
424 Boolean
425 VPNServiceRemoveAppRule(VPNServiceRef service, CFStringRef ruleIdentifier)
426 {
427 CFDictionaryRef vpn_config;
428
429 if (!isA_VPNService(service) || !isA_CFString(ruleIdentifier)) {
430 _SCErrorSet(kSCStatusInvalidArgument);
431 return FALSE;
432 }
433
434 vpn_config = SCNetworkInterfaceGetConfiguration(SCNetworkServiceGetInterface(service));
435 if (isA_CFDictionary(vpn_config)) {
436 CFIndex idx = find_app_rule(vpn_config, ruleIdentifier);
437 if (idx >= 0) {
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;
443
444 new_vpn_config = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, vpn_config);
445 new_app_rules = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, current_app_rules);
446
447 CFArrayRemoveValueAtIndex(new_app_rules, idx);
448 if (CFArrayGetCount(new_app_rules) > 0) {
449 CFDictionarySetValue(new_vpn_config, kSCPropNetVPNAppRules, new_app_rules);
450 } else {
451 CFDictionaryRemoveValue(new_vpn_config, kSCPropNetVPNAppRules);
452 }
453
454 SCNetworkInterfaceSetConfiguration(SCNetworkServiceGetInterface(service), new_vpn_config);
455
456 CFRelease(new_vpn_config);
457 CFRelease(new_app_rules);
458
459 return TRUE;
460 } else {
461 _SCErrorSet(kSCStatusFailed);
462 }
463 } else {
464 _SCErrorSet(kSCStatusNoKey);
465 }
466 } else {
467 _SCErrorSet(kSCStatusFailed);
468 }
469
470 return FALSE;
471 }
472
473
474 Boolean
475 VPNServiceIsManagedAppVPN(VPNServiceRef service)
476 {
477 Boolean result = FALSE;
478 CFStringRef mc_external_id = SCNetworkServiceCopyExternalID(service, CFSTR("MCVPNUUID"));
479 if (isA_CFString(mc_external_id)) {
480 result = TRUE;
481 }
482 if (mc_external_id != NULL) {
483 CFRelease(mc_external_id);
484 }
485 return result;
486 }
487