]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCProxies.c
9fe060d92d35bbe7e01ac750ba66721d6227ef41
[apple/configd.git] / SystemConfiguration.fproj / SCProxies.c
1 /*
2 * Copyright (c) 2000-2004, 2006-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * May 18, 2001 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31 #include <TargetConditionals.h>
32 #include <SystemConfiguration/SystemConfiguration.h>
33 #include <SystemConfiguration/SCValidation.h>
34 #include <SystemConfiguration/SCPrivate.h>
35 #include <SystemConfiguration/VPNAppLayerPrivate.h>
36
37 #include <netdb.h>
38
39
40
41
42 CFStringRef
43 SCDynamicStoreKeyCreateProxies(CFAllocatorRef allocator)
44 {
45 return SCDynamicStoreKeyCreateNetworkGlobalEntity(allocator,
46 kSCDynamicStoreDomainState,
47 kSCEntNetProxies);
48 }
49
50
51 static void
52 validate_proxy_content(CFMutableDictionaryRef proxies,
53 CFStringRef proxy_enable,
54 CFStringRef proxy_host,
55 CFStringRef proxy_port,
56 const char * proxy_service,
57 int proxy_defaultport)
58 {
59 int enabled = 0;
60 CFNumberRef num;
61
62 num = CFDictionaryGetValue(proxies, proxy_enable);
63 if (num != NULL) {
64 if (!isA_CFNumber(num) ||
65 !CFNumberGetValue(num, kCFNumberIntType, &enabled)) {
66 goto disable; // if we don't like the enabled key/value
67 }
68 }
69
70 if (proxy_host != NULL) {
71 CFStringRef host;
72
73 host = CFDictionaryGetValue(proxies, proxy_host);
74 if ((enabled == 0) && (host != NULL)) {
75 goto disable; // if not enabled, remove provided key/value
76 }
77
78 if ((enabled != 0) && !isA_CFString(host)) {
79 goto disable; // if enabled, not provided (or not valid)
80 }
81 }
82
83 if (proxy_port != NULL) {
84 CFNumberRef port;
85 int s_port = 0;
86
87 port = CFDictionaryGetValue(proxies, proxy_port);
88 if ((enabled == 0) && (port != NULL)) {
89 goto disable; // if not enabled, remove provided key/value
90 }
91
92 if ((enabled != 0) && (port != NULL)) {
93 if (!isA_CFNumber(port) ||
94 !CFNumberGetValue(port, kCFNumberIntType, &s_port) ||
95 (s_port > UINT16_MAX)) {
96 goto disable; // if enabled, not provided (or not valid)
97 }
98
99 if (s_port == 0) {
100 port = NULL; // if no port # provided, use default
101 }
102 }
103
104 if ((enabled != 0) && (port == NULL)) {
105 struct servent *service;
106
107 service = getservbyname(proxy_service, "tcp");
108 if (service != NULL) {
109 s_port = ntohs(service->s_port);
110 } else {
111 s_port = proxy_defaultport;
112 }
113 num = CFNumberCreate(NULL, kCFNumberIntType, &s_port);
114 CFDictionarySetValue(proxies, proxy_port, num);
115 CFRelease(num);
116 }
117 }
118
119 return;
120
121 disable :
122
123 enabled = 0;
124 num = CFNumberCreate(NULL, kCFNumberIntType, &enabled);
125 CFDictionarySetValue(proxies, proxy_enable, num);
126 CFRelease(num);
127 if (proxy_host != NULL) {
128 CFDictionaryRemoveValue(proxies, proxy_host);
129 }
130 if (proxy_port != NULL) {
131 CFDictionaryRemoveValue(proxies, proxy_port);
132 }
133
134 return;
135 }
136
137
138 static void
139 normalize_scoped_proxy(const void *key, const void *value, void *context);
140
141
142 static void
143 normalize_services_proxy(const void *key, const void *value, void *context);
144
145
146 static void
147 normalize_supplemental_proxy(const void *value, void *context);
148
149
150 static CF_RETURNS_RETAINED CFDictionaryRef
151 __SCNetworkProxiesCopyNormalized(CFDictionaryRef proxy)
152 {
153 CFArrayRef array;
154 CFMutableDictionaryRef newProxy;
155 CFNumberRef num;
156 CFDictionaryRef scoped;
157 CFDictionaryRef services;
158 CFArrayRef supplemental;
159
160 if (!isA_CFDictionary(proxy)) {
161 proxy = CFDictionaryCreate(NULL,
162 NULL,
163 NULL,
164 0,
165 &kCFTypeDictionaryKeyCallBacks,
166 &kCFTypeDictionaryValueCallBacks);
167 return proxy;
168 }
169
170 newProxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
171
172 validate_proxy_content(newProxy,
173 kSCPropNetProxiesFTPEnable,
174 kSCPropNetProxiesFTPProxy,
175 kSCPropNetProxiesFTPPort,
176 "ftp",
177 21);
178 validate_proxy_content(newProxy,
179 kSCPropNetProxiesGopherEnable,
180 kSCPropNetProxiesGopherProxy,
181 kSCPropNetProxiesGopherPort,
182 "gopher",
183 70);
184 validate_proxy_content(newProxy,
185 kSCPropNetProxiesHTTPEnable,
186 kSCPropNetProxiesHTTPProxy,
187 kSCPropNetProxiesHTTPPort,
188 "http",
189 80);
190 validate_proxy_content(newProxy,
191 kSCPropNetProxiesHTTPSEnable,
192 kSCPropNetProxiesHTTPSProxy,
193 kSCPropNetProxiesHTTPSPort,
194 "https",
195 443);
196 validate_proxy_content(newProxy,
197 kSCPropNetProxiesRTSPEnable,
198 kSCPropNetProxiesRTSPProxy,
199 kSCPropNetProxiesRTSPPort,
200 "rtsp",
201 554);
202 validate_proxy_content(newProxy,
203 kSCPropNetProxiesSOCKSEnable,
204 kSCPropNetProxiesSOCKSProxy,
205 kSCPropNetProxiesSOCKSPort,
206 "socks",
207 1080);
208 if (CFDictionaryContainsKey(newProxy, kSCPropNetProxiesProxyAutoConfigURLString)) {
209 validate_proxy_content(newProxy,
210 kSCPropNetProxiesProxyAutoConfigEnable,
211 kSCPropNetProxiesProxyAutoConfigURLString,
212 NULL,
213 NULL,
214 0);
215
216 // and we can't have both URLString and JavaScript keys
217 CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesProxyAutoConfigJavaScript);
218 } else {
219 validate_proxy_content(newProxy,
220 kSCPropNetProxiesProxyAutoConfigEnable,
221 kSCPropNetProxiesProxyAutoConfigJavaScript,
222 NULL,
223 NULL,
224 0);
225 }
226 validate_proxy_content(newProxy,
227 kSCPropNetProxiesProxyAutoDiscoveryEnable,
228 NULL,
229 NULL,
230 NULL,
231 0);
232
233 validate_proxy_content(newProxy,
234 kSCPropNetProxiesFallBackAllowed,
235 NULL,
236 NULL,
237 NULL,
238 0);
239
240 // validate FTP passive setting
241 num = CFDictionaryGetValue(newProxy, kSCPropNetProxiesFTPPassive);
242 if (num != NULL) {
243 int enabled = 0;
244
245 if (!isA_CFNumber(num) ||
246 !CFNumberGetValue(num, kCFNumberIntType, &enabled)) {
247 // if we don't like the enabled key/value
248 enabled = 1;
249 num = CFNumberCreate(NULL, kCFNumberIntType, &enabled);
250 CFDictionarySetValue(newProxy,
251 kSCPropNetProxiesFTPPassive,
252 num);
253 CFRelease(num);
254 }
255 }
256
257 // validate proxy exception list
258 array = CFDictionaryGetValue(newProxy, kSCPropNetProxiesExceptionsList);
259 if (array != NULL) {
260 CFIndex i;
261 CFIndex n;
262
263 n = isA_CFArray(array) ? CFArrayGetCount(array) : 0;
264 for (i = 0; i < n; i++) {
265 CFStringRef str;
266
267 str = CFArrayGetValueAtIndex(array, i);
268 if (!isA_CFString(str)) {
269 // if we don't like the array contents
270 n = 0;
271 break;
272 }
273 }
274
275 if (n == 0) {
276 CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesExceptionsList);
277 }
278 }
279
280 // validate exclude simple hostnames setting
281 num = CFDictionaryGetValue(newProxy, kSCPropNetProxiesExcludeSimpleHostnames);
282 if (num != NULL) {
283 int enabled;
284
285 if (!isA_CFNumber(num) ||
286 !CFNumberGetValue(num, kCFNumberIntType, &enabled)) {
287 // if we don't like the enabled key/value
288 enabled = 0;
289 num = CFNumberCreate(NULL, kCFNumberIntType, &enabled);
290 CFDictionarySetValue(newProxy,
291 kSCPropNetProxiesExcludeSimpleHostnames,
292 num);
293 CFRelease(num);
294 }
295 }
296
297 // cleanup scoped proxies
298 scoped = CFDictionaryGetValue(newProxy, kSCPropNetProxiesScoped);
299 if (isA_CFDictionary(scoped)) {
300 CFMutableDictionaryRef newScoped;
301
302 newScoped = CFDictionaryCreateMutable(NULL,
303 0,
304 &kCFTypeDictionaryKeyCallBacks,
305 &kCFTypeDictionaryValueCallBacks);
306 CFDictionaryApplyFunction(scoped,
307 normalize_scoped_proxy,
308 newScoped);
309 CFDictionarySetValue(newProxy, kSCPropNetProxiesScoped, newScoped);
310 CFRelease(newScoped);
311 }
312
313 // cleanup services proxies
314 services = CFDictionaryGetValue(newProxy, kSCPropNetProxiesServices);
315 if (isA_CFDictionary(services)) {
316 CFMutableDictionaryRef newServices;
317
318 newServices = CFDictionaryCreateMutable(NULL,
319 0,
320 &kCFTypeDictionaryKeyCallBacks,
321 &kCFTypeDictionaryValueCallBacks);
322 CFDictionaryApplyFunction(services,
323 normalize_services_proxy,
324 newServices);
325 CFDictionarySetValue(newProxy, kSCPropNetProxiesServices, newServices);
326 CFRelease(newServices);
327 }
328
329 // cleanup split/supplemental proxies
330 supplemental = CFDictionaryGetValue(newProxy, kSCPropNetProxiesSupplemental);
331 if (isA_CFArray(supplemental)) {
332 CFMutableArrayRef newSupplemental;
333
334 newSupplemental = CFArrayCreateMutable(NULL,
335 0,
336 &kCFTypeArrayCallBacks);
337 CFArrayApplyFunction(supplemental,
338 CFRangeMake(0, CFArrayGetCount(supplemental)),
339 normalize_supplemental_proxy,
340 newSupplemental);
341 CFDictionarySetValue(newProxy, kSCPropNetProxiesSupplemental, newSupplemental);
342 CFRelease(newSupplemental);
343 }
344
345 proxy = CFDictionaryCreateCopy(NULL,newProxy);
346 CFRelease(newProxy);
347
348 return proxy;
349 }
350
351
352 static void
353 normalize_scoped_proxy(const void *key, const void *value, void *context)
354 {
355 CFStringRef interface = (CFStringRef)key;
356 CFDictionaryRef proxy = (CFDictionaryRef)value;
357 CFMutableDictionaryRef newScoped = (CFMutableDictionaryRef)context;
358
359 proxy = __SCNetworkProxiesCopyNormalized(proxy);
360 CFDictionarySetValue(newScoped, interface, proxy);
361 CFRelease(proxy);
362
363 return;
364 }
365
366 static void
367 normalize_services_proxy(const void *key, const void *value, void *context)
368 {
369 CFStringRef serviceID = (CFStringRef)key;
370 CFDictionaryRef proxy = (CFDictionaryRef)value;
371 CFMutableDictionaryRef newServices = (CFMutableDictionaryRef)context;
372
373 proxy = __SCNetworkProxiesCopyNormalized(proxy);
374 CFDictionarySetValue(newServices, serviceID, proxy);
375 CFRelease(proxy);
376
377 return;
378 }
379
380 static void
381 normalize_supplemental_proxy(const void *value, void *context)
382 {
383 CFDictionaryRef proxy = (CFDictionaryRef)value;
384 CFMutableArrayRef newSupplemental = (CFMutableArrayRef)context;
385
386 proxy = __SCNetworkProxiesCopyNormalized(proxy);
387 CFArrayAppendValue(newSupplemental, proxy);
388 CFRelease(proxy);
389
390 return;
391 }
392
393 CFDictionaryRef
394 SCDynamicStoreCopyProxies(SCDynamicStoreRef store)
395 {
396 return SCDynamicStoreCopyProxiesWithOptions(store, NULL);
397 }
398
399 const CFStringRef kSCProxiesNoGlobal = CFSTR("NO_GLOBAL");
400
401 CFDictionaryRef
402 SCDynamicStoreCopyProxiesWithOptions(SCDynamicStoreRef store, CFDictionaryRef options)
403 {
404 Boolean bypass = FALSE;
405 CFStringRef key;
406 CFDictionaryRef proxies;
407
408 if (options != NULL) {
409 CFBooleanRef bypassGlobalOption;
410
411 if (isA_CFDictionary(options) == NULL) {
412 _SCErrorSet(kSCStatusInvalidArgument);
413 return NULL;
414 }
415
416 bypassGlobalOption = CFDictionaryGetValue(options, kSCProxiesNoGlobal);
417 if (isA_CFBoolean(bypassGlobalOption) && CFBooleanGetValue(bypassGlobalOption)) {
418 bypass = TRUE;
419 }
420 }
421
422
423 /* copy proxy information from dynamic store */
424
425 key = SCDynamicStoreKeyCreateProxies(NULL);
426 proxies = SCDynamicStoreCopyValue(store, key);
427 CFRelease(key);
428
429
430 if (proxies != NULL) {
431 CFDictionaryRef base = proxies;
432
433 proxies = __SCNetworkProxiesCopyNormalized(base);
434 CFRelease(base);
435 } else {
436 proxies = CFDictionaryCreate(NULL,
437 NULL,
438 NULL,
439 0,
440 &kCFTypeDictionaryKeyCallBacks,
441 &kCFTypeDictionaryValueCallBacks);
442 }
443
444
445 return proxies;
446 }
447
448
449 CFArrayRef
450 SCNetworkProxiesCopyMatching(CFDictionaryRef globalConfiguration,
451 CFStringRef server,
452 CFStringRef interface)
453 {
454 CFMutableDictionaryRef newProxy;
455 static const audit_token_t null_audit = KERNEL_AUDIT_TOKEN_VALUE;
456 UUID_DEFINE(null_uuid, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
457 CFArrayRef proxies = NULL;
458 CFDictionaryRef proxy;
459 int sc_status = kSCStatusOK;
460 CFStringRef serviceID;
461 CFStringRef trimmed = NULL;
462
463 if (!isA_CFDictionary(globalConfiguration)) {
464 // if no proxy configuration
465 _SCErrorSet(kSCStatusOK);
466 return NULL;
467 }
468
469 if (interface != NULL) {
470 CFDictionaryRef scoped;
471
472 if (!isA_CFString(interface) ||
473 (CFStringGetLength(interface) == 0)) {
474 _SCErrorSet(kSCStatusInvalidArgument);
475 return NULL;
476 }
477
478 scoped = CFDictionaryGetValue(globalConfiguration, kSCPropNetProxiesScoped);
479 if (scoped == NULL) {
480 // if no scoped proxy configurations
481 _SCErrorSet(kSCStatusOK);
482 return NULL;
483 }
484
485 if (!isA_CFDictionary(scoped)) {
486 // if corrupt proxy configuration
487 _SCErrorSet(kSCStatusFailed);
488 return NULL;
489 }
490
491 proxy = CFDictionaryGetValue(scoped, interface);
492 if (proxy == NULL) {
493 // if no scoped proxy configuration for this interface
494 _SCErrorSet(kSCStatusOK);
495 return NULL;
496 }
497
498 if (!isA_CFDictionary(proxy)) {
499 // if corrupt proxy configuration
500 _SCErrorSet(kSCStatusFailed);
501 return NULL;
502 }
503
504 // return per-interface proxy configuration
505 proxies = CFArrayCreate(NULL, (const void **)&proxy, 1, &kCFTypeArrayCallBacks);
506 return proxies;
507 }
508
509 // Check for app-layer VPN proxy results (with or without server)
510 serviceID = VPNAppLayerCopyMatchingService(null_audit, 0, null_uuid, NULL, server, NULL, NULL);
511 if (serviceID != NULL) {
512 CFDictionaryRef serviceProxies = NULL;
513
514 serviceProxies = CFDictionaryGetValue(globalConfiguration, kSCPropNetProxiesServices);
515 if (serviceProxies == NULL) {
516 _SCErrorSet(kSCStatusOK);
517 CFRelease(serviceID);
518 goto app_layer_no_proxies;
519 }
520 if (!isA_CFDictionary(serviceProxies)) {
521 _SCErrorSet(kSCStatusFailed);
522 CFRelease(serviceID);
523 goto app_layer_no_proxies;
524 }
525
526 proxy = CFDictionaryGetValue(serviceProxies, serviceID);
527 CFRelease(serviceID);
528 if (proxy == NULL) {
529 _SCErrorSet(kSCStatusOK);
530 goto app_layer_no_proxies;
531 }
532 if (!isA_CFDictionary(proxy)) {
533 _SCErrorSet(kSCStatusFailed);
534 goto app_layer_no_proxies;
535 }
536
537 proxies = CFArrayCreate(NULL, (const void **)&proxy, 1, &kCFTypeArrayCallBacks);
538 return proxies;
539
540 app_layer_no_proxies:
541
542 /*
543 * Rather than returning NULL, return an empty proxy configuration.
544 * This ensures that the global proxy configuration will not be used.
545 */
546 proxy = CFDictionaryCreate(NULL, NULL, NULL, 0, NULL, NULL);
547 proxies = CFArrayCreate(NULL, (const void **)&proxy, 1, &kCFTypeArrayCallBacks);
548 CFRelease(proxy);
549 return proxies;
550 }
551
552 if (server != NULL) {
553 CFIndex i;
554 CFMutableArrayRef matching = NULL;
555 CFIndex n = 0;
556 CFIndex server_len;
557 CFArrayRef supplemental;
558
559 trimmed = _SC_trimDomain(server);
560 if (trimmed == NULL) {
561 _SCErrorSet(kSCStatusInvalidArgument);
562 return NULL;
563 }
564
565 server = trimmed;
566 server_len = CFStringGetLength(server);
567
568 supplemental = CFDictionaryGetValue(globalConfiguration, kSCPropNetProxiesSupplemental);
569 if (supplemental != NULL) {
570 if (!isA_CFArray(supplemental)) {
571 // if corrupt proxy configuration
572 sc_status = kSCStatusFailed;
573 goto done;
574 }
575
576 n = CFArrayGetCount(supplemental);
577 }
578
579 for (i = 0; i < n; i++) {
580 CFStringRef domain;
581 CFIndex domain_len;
582 CFIndex n_matching;
583
584 proxy = CFArrayGetValueAtIndex(supplemental, i);
585 if (!isA_CFDictionary(proxy)) {
586 // if corrupt proxy configuration
587 continue;
588 }
589
590 domain = CFDictionaryGetValue(proxy, kSCPropNetProxiesSupplementalMatchDomain);
591 if (!isA_CFString(domain)) {
592 // if corrupt proxy configuration
593 continue;
594 }
595
596 domain_len = CFStringGetLength(domain);
597 if (domain_len > 0) {
598 if (!CFStringFindWithOptions(server,
599 domain,
600 CFRangeMake(0, server_len),
601 kCFCompareCaseInsensitive|kCFCompareAnchored|kCFCompareBackwards,
602 NULL)) {
603 // if server does not match this proxy domain (or host)
604 continue;
605 }
606
607 if ((server_len > domain_len) &&
608 !CFStringFindWithOptions(server,
609 CFSTR("."),
610 CFRangeMake(0, server_len - domain_len),
611 kCFCompareCaseInsensitive|kCFCompareAnchored|kCFCompareBackwards,
612 NULL)) {
613 // if server does not match this proxy domain
614 continue;
615 }
616 // } else {
617 // // if this is a "default" (match all) proxy domain
618 }
619
620 if (matching == NULL) {
621 matching = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
622 }
623 n_matching = CFArrayGetCount(matching);
624
625 newProxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
626 CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchDomain);
627 if ((n_matching == 0) ||
628 !CFArrayContainsValue(matching, CFRangeMake(0, n_matching), newProxy)) {
629 // add this matching proxy
630 CFArrayAppendValue(matching, newProxy);
631 }
632 CFRelease(newProxy);
633 }
634
635 if (matching != NULL) {
636 // if we have any supplemental match domains
637 proxies = CFArrayCreateCopy(NULL, matching);
638 CFRelease(matching);
639 goto done;
640 }
641 }
642
643 // no matches, return "global" proxy configuration
644
645 newProxy = CFDictionaryCreateMutableCopy(NULL, 0, globalConfiguration);
646 CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesScoped);
647 CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesServices);
648 CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplemental);
649 proxies = CFArrayCreate(NULL, (const void **)&newProxy, 1, &kCFTypeArrayCallBacks);
650 CFRelease(newProxy);
651
652 done :
653
654 if (sc_status != kSCStatusOK) {
655 if (proxies != NULL) {
656 CFRelease(proxies);
657 proxies = NULL;
658 }
659 _SCErrorSet(sc_status);
660 }
661 if (trimmed != NULL) CFRelease(trimmed);
662
663 return proxies;
664 }