2 * Copyright (c) 2000-2004, 2006-2013, 2015, 2016 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * May 18, 2001 Allan Nathanson <ajn@apple.com>
31 #include <TargetConditionals.h>
32 #include <SystemConfiguration/SystemConfiguration.h>
33 #include <SystemConfiguration/SCValidation.h>
34 #include <SystemConfiguration/SCPrivate.h>
35 #include <SystemConfiguration/VPNAppLayerPrivate.h>
38 #if !TARGET_OS_SIMULATOR
39 #include <ne_session.h>
40 #endif // !TARGET_OS_SIMULATOR
43 SCDynamicStoreKeyCreateProxies(CFAllocatorRef allocator
)
45 return SCDynamicStoreKeyCreateNetworkGlobalEntity(allocator
,
46 kSCDynamicStoreDomainState
,
52 validate_proxy_content(CFMutableDictionaryRef proxies
,
53 CFStringRef proxy_enable_key
,
54 CFStringRef proxy_host_key
,
55 CFStringRef proxy_port_key
,
56 const char * proxy_service_name
,
57 int proxy_defaultport
,
58 Boolean multiple_proxies
)
63 num
= CFDictionaryGetValue(proxies
, proxy_enable_key
);
65 if (!isA_CFNumber(num
) ||
66 !CFNumberGetValue(num
, kCFNumberIntType
, &enabled
)) {
67 goto disable
; // if we don't like the enabled key/value
71 if (proxy_host_key
!= NULL
) {
74 host_val
= CFDictionaryGetValue(proxies
, proxy_host_key
);
75 if ((enabled
== 0) && (host_val
!= NULL
)) {
76 goto disable
; // if not enabled, remove provided key/value
80 if (isA_CFString(host_val
)) {
81 CFStringRef host
= (CFStringRef
)host_val
;
83 if (multiple_proxies
) {
84 goto disable
; // if multiple proxies expected
87 if (CFStringGetLength(host
) == 0) {
88 goto disable
; // if proxy host string not valid
90 } else if (isA_CFArray(host_val
)) {
91 CFArrayRef hosts
= (CFArrayRef
)host_val
;
94 if (!multiple_proxies
) {
95 goto disable
; // if single proxy expected
98 n
= CFArrayGetCount(hosts
);
100 goto disable
; // if no hosts provided
103 for (CFIndex i
= 0; i
< n
; i
++) {
104 CFStringRef host
= CFArrayGetValueAtIndex(hosts
, i
);
106 if (!isA_CFString(host
) || (CFStringGetLength(host
) == 0)) {
107 goto disable
; // if proxy host string not valid
111 goto disable
; // not valid
116 if (proxy_port_key
!= NULL
) {
120 port
= CFDictionaryGetValue(proxies
, proxy_port_key
);
121 if ((enabled
== 0) && (port
!= NULL
)) {
122 goto disable
; // if not enabled, remove provided key/value
125 if ((enabled
!= 0) && (port
!= NULL
)) {
126 if (!isA_CFNumber(port
) ||
127 !CFNumberGetValue(port
, kCFNumberIntType
, &s_port
) ||
128 (s_port
> UINT16_MAX
)) {
129 goto disable
; // if enabled, not provided (or not valid)
133 port
= NULL
; // if no port # provided, use default
137 if ((enabled
!= 0) && (port
== NULL
)) {
138 struct servent
*service
;
140 if (proxy_service_name
== NULL
) {
141 goto disable
; // no "default" port available
144 service
= getservbyname(proxy_service_name
, "tcp");
145 if (service
!= NULL
) {
146 s_port
= ntohs(service
->s_port
);
148 s_port
= proxy_defaultport
;
150 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &s_port
);
151 CFDictionarySetValue(proxies
, proxy_port_key
, num
);
161 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &enabled
);
162 CFDictionarySetValue(proxies
, proxy_enable_key
, num
);
164 if (proxy_host_key
!= NULL
) {
165 CFDictionaryRemoveValue(proxies
, proxy_host_key
);
167 if (proxy_port_key
!= NULL
) {
168 CFDictionaryRemoveValue(proxies
, proxy_port_key
);
176 normalize_scoped_proxy(const void *key
, const void *value
, void *context
);
180 normalize_services_proxy(const void *key
, const void *value
, void *context
);
184 normalize_supplemental_proxy(const void *value
, void *context
);
187 static CF_RETURNS_RETAINED CFDictionaryRef
188 __SCNetworkProxiesCopyNormalized(CFDictionaryRef proxy
)
191 CFMutableDictionaryRef newProxy
;
193 CFDictionaryRef scoped
;
194 CFDictionaryRef services
;
195 CFArrayRef supplemental
;
197 if (!isA_CFDictionary(proxy
)) {
198 proxy
= CFDictionaryCreate(NULL
,
202 &kCFTypeDictionaryKeyCallBacks
,
203 &kCFTypeDictionaryValueCallBacks
);
207 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, proxy
);
209 validate_proxy_content(newProxy
,
210 kSCPropNetProxiesFTPEnable
,
211 kSCPropNetProxiesFTPProxy
,
212 kSCPropNetProxiesFTPPort
,
216 validate_proxy_content(newProxy
,
217 kSCPropNetProxiesGopherEnable
,
218 kSCPropNetProxiesGopherProxy
,
219 kSCPropNetProxiesGopherPort
,
223 validate_proxy_content(newProxy
,
224 kSCPropNetProxiesHTTPEnable
,
225 kSCPropNetProxiesHTTPProxy
,
226 kSCPropNetProxiesHTTPPort
,
230 validate_proxy_content(newProxy
,
231 kSCPropNetProxiesHTTPSEnable
,
232 kSCPropNetProxiesHTTPSProxy
,
233 kSCPropNetProxiesHTTPSPort
,
237 validate_proxy_content(newProxy
,
238 kSCPropNetProxiesRTSPEnable
,
239 kSCPropNetProxiesRTSPProxy
,
240 kSCPropNetProxiesRTSPPort
,
244 validate_proxy_content(newProxy
,
245 kSCPropNetProxiesSOCKSEnable
,
246 kSCPropNetProxiesSOCKSProxy
,
247 kSCPropNetProxiesSOCKSPort
,
251 validate_proxy_content(newProxy
,
252 kSCPropNetProxiesTransportConverterEnable
,
253 kSCPropNetProxiesTransportConverterProxy
,
254 kSCPropNetProxiesTransportConverterPort
,
258 if (CFDictionaryContainsKey(newProxy
, kSCPropNetProxiesProxyAutoConfigURLString
)) {
259 validate_proxy_content(newProxy
,
260 kSCPropNetProxiesProxyAutoConfigEnable
,
261 kSCPropNetProxiesProxyAutoConfigURLString
,
267 // and we can't have both URLString and JavaScript keys
268 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
270 validate_proxy_content(newProxy
,
271 kSCPropNetProxiesProxyAutoConfigEnable
,
272 kSCPropNetProxiesProxyAutoConfigJavaScript
,
278 validate_proxy_content(newProxy
,
279 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
286 validate_proxy_content(newProxy
,
287 kSCPropNetProxiesFallBackAllowed
,
294 // validate FTP passive setting
295 num
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesFTPPassive
);
299 if (!isA_CFNumber(num
) ||
300 !CFNumberGetValue(num
, kCFNumberIntType
, &enabled
)) {
301 // if we don't like the enabled key/value
303 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &enabled
);
304 CFDictionarySetValue(newProxy
,
305 kSCPropNetProxiesFTPPassive
,
311 // validate proxy exception list
312 array
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesExceptionsList
);
317 n
= isA_CFArray(array
) ? CFArrayGetCount(array
) : 0;
318 for (i
= 0; i
< n
; i
++) {
321 str
= CFArrayGetValueAtIndex(array
, i
);
322 if (!isA_CFString(str
) || (CFStringGetLength(str
) == 0)) {
323 // if we don't like the array contents
330 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesExceptionsList
);
334 // validate exclude simple hostnames setting
335 num
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesExcludeSimpleHostnames
);
339 if (!isA_CFNumber(num
) ||
340 !CFNumberGetValue(num
, kCFNumberIntType
, &enabled
)) {
341 // if we don't like the enabled key/value
343 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &enabled
);
344 CFDictionarySetValue(newProxy
,
345 kSCPropNetProxiesExcludeSimpleHostnames
,
351 // cleanup scoped proxies
352 scoped
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesScoped
);
353 if (isA_CFDictionary(scoped
)) {
354 CFMutableDictionaryRef newScoped
;
356 newScoped
= CFDictionaryCreateMutable(NULL
,
358 &kCFTypeDictionaryKeyCallBacks
,
359 &kCFTypeDictionaryValueCallBacks
);
360 CFDictionaryApplyFunction(scoped
,
361 normalize_scoped_proxy
,
363 CFDictionarySetValue(newProxy
, kSCPropNetProxiesScoped
, newScoped
);
364 CFRelease(newScoped
);
367 // cleanup services proxies
368 services
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesServices
);
369 if (isA_CFDictionary(services
)) {
370 CFMutableDictionaryRef newServices
;
372 newServices
= CFDictionaryCreateMutable(NULL
,
374 &kCFTypeDictionaryKeyCallBacks
,
375 &kCFTypeDictionaryValueCallBacks
);
376 CFDictionaryApplyFunction(services
,
377 normalize_services_proxy
,
379 CFDictionarySetValue(newProxy
, kSCPropNetProxiesServices
, newServices
);
380 CFRelease(newServices
);
383 // cleanup split/supplemental proxies
384 supplemental
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesSupplemental
);
385 if (isA_CFArray(supplemental
)) {
386 CFMutableArrayRef newSupplemental
;
388 newSupplemental
= CFArrayCreateMutable(NULL
,
390 &kCFTypeArrayCallBacks
);
391 CFArrayApplyFunction(supplemental
,
392 CFRangeMake(0, CFArrayGetCount(supplemental
)),
393 normalize_supplemental_proxy
,
395 CFDictionarySetValue(newProxy
, kSCPropNetProxiesSupplemental
, newSupplemental
);
396 CFRelease(newSupplemental
);
399 proxy
= CFDictionaryCreateCopy(NULL
,newProxy
);
407 normalize_scoped_proxy(const void *key
, const void *value
, void *context
)
409 CFStringRef interface
= (CFStringRef
)key
;
410 CFDictionaryRef proxy
= (CFDictionaryRef
)value
;
411 CFMutableDictionaryRef newScoped
= (CFMutableDictionaryRef
)context
;
413 proxy
= __SCNetworkProxiesCopyNormalized(proxy
);
414 CFDictionarySetValue(newScoped
, interface
, proxy
);
421 normalize_services_proxy(const void *key
, const void *value
, void *context
)
423 CFNumberRef serviceIndex
= (CFNumberRef
)key
;
424 CFDictionaryRef proxy
= (CFDictionaryRef
)value
;
425 CFMutableDictionaryRef newServices
= (CFMutableDictionaryRef
)context
;
427 proxy
= __SCNetworkProxiesCopyNormalized(proxy
);
428 CFDictionarySetValue(newServices
, serviceIndex
, proxy
);
435 normalize_supplemental_proxy(const void *value
, void *context
)
437 CFDictionaryRef proxy
= (CFDictionaryRef
)value
;
438 CFMutableArrayRef newSupplemental
= (CFMutableArrayRef
)context
;
440 proxy
= __SCNetworkProxiesCopyNormalized(proxy
);
441 CFArrayAppendValue(newSupplemental
, proxy
);
448 SCDynamicStoreCopyProxies(SCDynamicStoreRef store
)
450 return SCDynamicStoreCopyProxiesWithOptions(store
, NULL
);
453 const CFStringRef kSCProxiesNoGlobal
= CFSTR("NO_GLOBAL");
456 SCDynamicStoreCopyProxiesWithOptions(SCDynamicStoreRef store
, CFDictionaryRef options
)
458 Boolean bypass
= FALSE
;
460 CFDictionaryRef proxies
= NULL
;
462 if (options
!= NULL
) {
463 CFBooleanRef bypassGlobalOption
;
465 if (!isA_CFDictionary(options
)) {
466 _SCErrorSet(kSCStatusInvalidArgument
);
470 bypassGlobalOption
= CFDictionaryGetValue(options
, kSCProxiesNoGlobal
);
471 if (isA_CFBoolean(bypassGlobalOption
) && CFBooleanGetValue(bypassGlobalOption
)) {
477 /* copy proxy information from dynamic store */
479 key
= SCDynamicStoreKeyCreateProxies(NULL
);
480 proxies
= SCDynamicStoreCopyValue(store
, key
);
483 if (isA_CFDictionary(proxies
) &&
484 CFDictionaryContainsKey(proxies
, kSCPropNetProxiesBypassAllowed
)) {
485 CFMutableDictionaryRef newProxies
;
487 newProxies
= CFDictionaryCreateMutableCopy(NULL
, 0, proxies
);
491 * Remove kSCPropNetProxiesBypassAllowed property from network
492 * service based configurations.
494 CFDictionaryRemoveValue(newProxies
, kSCPropNetProxiesBypassAllowed
);
495 proxies
= newProxies
;
499 if (proxies
!= NULL
) {
500 CFDictionaryRef base
= proxies
;
502 proxies
= __SCNetworkProxiesCopyNormalized(base
);
505 proxies
= CFDictionaryCreate(NULL
,
509 &kCFTypeDictionaryKeyCallBacks
,
510 &kCFTypeDictionaryValueCallBacks
);
518 _SCNetworkProxiesCopyMatchingInternal(CFDictionaryRef globalConfiguration
,
520 CFStringRef interface
,
521 CFDictionaryRef options
)
523 CFMutableDictionaryRef newProxy
;
525 CFArrayRef proxies
= NULL
;
526 CFDictionaryRef proxy
;
527 int sc_status
= kSCStatusOK
;
528 CFStringRef trimmed
= NULL
;
531 if (!isA_CFDictionary(globalConfiguration
)) {
532 // if no proxy configuration
533 _SCErrorSet(kSCStatusOK
);
537 uuid_clear(match_uuid
);
539 if (isA_CFDictionary(options
)) {
542 interface
= CFDictionaryGetValue(options
, kSCProxiesMatchInterface
);
543 interface
= isA_CFString(interface
);
545 server
= CFDictionaryGetValue(options
, kSCProxiesMatchServer
);
546 server
= isA_CFString(server
);
548 euuid
= CFDictionaryGetValue(options
, kSCProxiesMatchExecutableUUID
);
549 euuid
= isA_CFType(euuid
, CFUUIDGetTypeID());
551 CFUUIDBytes uuid_bytes
= CFUUIDGetUUIDBytes(euuid
);
552 uuid_copy(match_uuid
, (const uint8_t *)&uuid_bytes
);
556 if (interface
!= NULL
) {
557 CFDictionaryRef scoped
;
559 if (!isA_CFString(interface
) ||
560 (CFStringGetLength(interface
) == 0)) {
561 _SCErrorSet(kSCStatusInvalidArgument
);
565 scoped
= CFDictionaryGetValue(globalConfiguration
, kSCPropNetProxiesScoped
);
566 if (scoped
== NULL
) {
567 #if !TARGET_OS_SIMULATOR
568 if (CFDictionaryContainsKey(globalConfiguration
, kSCPropNetProxiesBypassAllowed
) &&
569 ne_session_always_on_vpn_configs_present()) {
571 * The kSCPropNetProxiesBypassAllowed key will be present
572 * for managed proxy configurations where bypassing is *not*
575 * Also (for now), forcing the use of the managed proxy
576 * configurations will only be done with AOVPN present.
580 #endif // !TARGET_OS_SIMULATOR
582 // if no scoped proxy configurations
583 _SCErrorSet(kSCStatusOK
);
587 if (!isA_CFDictionary(scoped
)) {
588 // if corrupt proxy configuration
589 _SCErrorSet(kSCStatusFailed
);
593 proxy
= CFDictionaryGetValue(scoped
, interface
);
595 // if no scoped proxy configuration for this interface
596 _SCErrorSet(kSCStatusOK
);
600 if (!isA_CFDictionary(proxy
)) {
601 // if corrupt proxy configuration
602 _SCErrorSet(kSCStatusFailed
);
606 // return per-interface proxy configuration
607 proxies
= CFArrayCreate(NULL
, (const void **)&proxy
, 1, &kCFTypeArrayCallBacks
);
612 if (server
!= NULL
) {
614 CFMutableArrayRef matching
= NULL
;
617 CFArrayRef supplemental
;
619 trimmed
= _SC_trimDomain(server
);
620 if (trimmed
== NULL
) {
621 _SCErrorSet(kSCStatusInvalidArgument
);
626 server_len
= CFStringGetLength(server
);
628 supplemental
= CFDictionaryGetValue(globalConfiguration
, kSCPropNetProxiesSupplemental
);
629 if (supplemental
!= NULL
) {
630 if (!isA_CFArray(supplemental
)) {
631 // if corrupt proxy configuration
632 sc_status
= kSCStatusFailed
;
636 n
= CFArrayGetCount(supplemental
);
639 for (i
= 0; i
< n
; i
++) {
644 proxy
= CFArrayGetValueAtIndex(supplemental
, i
);
645 if (!isA_CFDictionary(proxy
)) {
646 // if corrupt proxy configuration
650 domain
= CFDictionaryGetValue(proxy
, kSCPropNetProxiesSupplementalMatchDomain
);
651 if (!isA_CFString(domain
)) {
652 // if corrupt proxy configuration
656 domain_len
= CFStringGetLength(domain
);
657 if (domain_len
> 0) {
658 if (!CFStringFindWithOptions(server
,
660 CFRangeMake(0, server_len
),
661 kCFCompareCaseInsensitive
|kCFCompareAnchored
|kCFCompareBackwards
,
663 // if server does not match this proxy domain (or host)
667 if ((server_len
> domain_len
) &&
668 !CFStringFindWithOptions(server
,
670 CFRangeMake(0, server_len
- domain_len
),
671 kCFCompareCaseInsensitive
|kCFCompareAnchored
|kCFCompareBackwards
,
673 // if server does not match this proxy domain
677 // // if this is a "default" (match all) proxy domain
680 if (matching
== NULL
) {
681 matching
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
683 n_matching
= CFArrayGetCount(matching
);
685 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, proxy
);
686 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesSupplementalMatchDomain
);
687 if ((n_matching
== 0) ||
688 !CFArrayContainsValue(matching
, CFRangeMake(0, n_matching
), newProxy
)) {
689 // add this matching proxy
690 CFArrayAppendValue(matching
, newProxy
);
695 if (matching
!= NULL
) {
696 // if we have any supplemental match domains
697 proxies
= CFArrayCreateCopy(NULL
, matching
);
703 // no matches, return "global" proxy configuration
705 #if !TARGET_OS_SIMULATOR
707 #endif // !TARGET_OS_SIMULATOR
709 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, globalConfiguration
);
710 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesScoped
);
711 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesServices
);
712 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesSupplemental
);
713 proxies
= CFArrayCreate(NULL
, (const void **)&newProxy
, 1, &kCFTypeArrayCallBacks
);
718 if (sc_status
!= kSCStatusOK
) {
719 _SCErrorSet(sc_status
);
721 // Note: if we are returning an error then we must
722 // return w/proxies==NULL. At present, there
723 // is no code (above) that would get here with
724 // proxies!=NULL so we don't need to take any
725 // action but future coder's should beware :-)
726 // if (proxies != NULL) {
727 // CFRelease(proxies);
731 if (trimmed
!= NULL
) CFRelease(trimmed
);
737 SCNetworkProxiesCreateProxyAgentData(CFDictionaryRef proxyConfig
)
739 CFDataRef result
= NULL
;
740 CFArrayRef newProxy
= NULL
;
742 if (!isA_CFDictionary(proxyConfig
)) {
743 SC_log(LOG_ERR
, "Invalid proxy configuration");
744 _SCErrorSet(kSCStatusInvalidArgument
);
748 newProxy
= CFArrayCreate(NULL
, (const void **)&proxyConfig
, 1, &kCFTypeArrayCallBacks
);
749 (void)_SCSerialize(newProxy
, &result
, NULL
, NULL
);
756 SCNetworkProxiesCopyMatching(CFDictionaryRef globalConfiguration
,
758 CFStringRef interface
)
760 return _SCNetworkProxiesCopyMatchingInternal(globalConfiguration
, server
, interface
, NULL
);
764 SCNetworkProxiesCopyMatchingWithOptions(CFDictionaryRef globalConfiguration
,
765 CFDictionaryRef options
)
767 return _SCNetworkProxiesCopyMatchingInternal(globalConfiguration
, NULL
, NULL
, options
);