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
,
54 CFStringRef proxy_host
,
55 CFStringRef proxy_port
,
56 const char * proxy_service
,
57 int proxy_defaultport
)
62 num
= CFDictionaryGetValue(proxies
, proxy_enable
);
64 if (!isA_CFNumber(num
) ||
65 !CFNumberGetValue(num
, kCFNumberIntType
, &enabled
)) {
66 goto disable
; // if we don't like the enabled key/value
70 if (proxy_host
!= NULL
) {
73 host
= CFDictionaryGetValue(proxies
, proxy_host
);
74 if ((enabled
== 0) && (host
!= NULL
)) {
75 goto disable
; // if not enabled, remove provided key/value
79 (!isA_CFString(host
) || (CFStringGetLength(host
) == 0))) {
80 goto disable
; // if enabled, not provided (or not valid)
84 if (proxy_port
!= NULL
) {
88 port
= CFDictionaryGetValue(proxies
, proxy_port
);
89 if ((enabled
== 0) && (port
!= NULL
)) {
90 goto disable
; // if not enabled, remove provided key/value
93 if ((enabled
!= 0) && (port
!= NULL
)) {
94 if (!isA_CFNumber(port
) ||
95 !CFNumberGetValue(port
, kCFNumberIntType
, &s_port
) ||
96 (s_port
> UINT16_MAX
)) {
97 goto disable
; // if enabled, not provided (or not valid)
101 port
= NULL
; // if no port # provided, use default
105 if ((enabled
!= 0) && (port
== NULL
)) {
106 struct servent
*service
;
108 service
= getservbyname(proxy_service
, "tcp");
109 if (service
!= NULL
) {
110 s_port
= ntohs(service
->s_port
);
112 s_port
= proxy_defaultport
;
114 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &s_port
);
115 CFDictionarySetValue(proxies
, proxy_port
, num
);
125 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &enabled
);
126 CFDictionarySetValue(proxies
, proxy_enable
, num
);
128 if (proxy_host
!= NULL
) {
129 CFDictionaryRemoveValue(proxies
, proxy_host
);
131 if (proxy_port
!= NULL
) {
132 CFDictionaryRemoveValue(proxies
, proxy_port
);
140 normalize_scoped_proxy(const void *key
, const void *value
, void *context
);
144 normalize_services_proxy(const void *key
, const void *value
, void *context
);
148 normalize_supplemental_proxy(const void *value
, void *context
);
151 static CF_RETURNS_RETAINED CFDictionaryRef
152 __SCNetworkProxiesCopyNormalized(CFDictionaryRef proxy
)
155 CFMutableDictionaryRef newProxy
;
157 CFDictionaryRef scoped
;
158 CFDictionaryRef services
;
159 CFArrayRef supplemental
;
161 if (!isA_CFDictionary(proxy
)) {
162 proxy
= CFDictionaryCreate(NULL
,
166 &kCFTypeDictionaryKeyCallBacks
,
167 &kCFTypeDictionaryValueCallBacks
);
171 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, proxy
);
173 validate_proxy_content(newProxy
,
174 kSCPropNetProxiesFTPEnable
,
175 kSCPropNetProxiesFTPProxy
,
176 kSCPropNetProxiesFTPPort
,
179 validate_proxy_content(newProxy
,
180 kSCPropNetProxiesGopherEnable
,
181 kSCPropNetProxiesGopherProxy
,
182 kSCPropNetProxiesGopherPort
,
185 validate_proxy_content(newProxy
,
186 kSCPropNetProxiesHTTPEnable
,
187 kSCPropNetProxiesHTTPProxy
,
188 kSCPropNetProxiesHTTPPort
,
191 validate_proxy_content(newProxy
,
192 kSCPropNetProxiesHTTPSEnable
,
193 kSCPropNetProxiesHTTPSProxy
,
194 kSCPropNetProxiesHTTPSPort
,
197 validate_proxy_content(newProxy
,
198 kSCPropNetProxiesRTSPEnable
,
199 kSCPropNetProxiesRTSPProxy
,
200 kSCPropNetProxiesRTSPPort
,
203 validate_proxy_content(newProxy
,
204 kSCPropNetProxiesSOCKSEnable
,
205 kSCPropNetProxiesSOCKSProxy
,
206 kSCPropNetProxiesSOCKSPort
,
209 if (CFDictionaryContainsKey(newProxy
, kSCPropNetProxiesProxyAutoConfigURLString
)) {
210 validate_proxy_content(newProxy
,
211 kSCPropNetProxiesProxyAutoConfigEnable
,
212 kSCPropNetProxiesProxyAutoConfigURLString
,
217 // and we can't have both URLString and JavaScript keys
218 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
220 validate_proxy_content(newProxy
,
221 kSCPropNetProxiesProxyAutoConfigEnable
,
222 kSCPropNetProxiesProxyAutoConfigJavaScript
,
227 validate_proxy_content(newProxy
,
228 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
234 validate_proxy_content(newProxy
,
235 kSCPropNetProxiesFallBackAllowed
,
241 // validate FTP passive setting
242 num
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesFTPPassive
);
246 if (!isA_CFNumber(num
) ||
247 !CFNumberGetValue(num
, kCFNumberIntType
, &enabled
)) {
248 // if we don't like the enabled key/value
250 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &enabled
);
251 CFDictionarySetValue(newProxy
,
252 kSCPropNetProxiesFTPPassive
,
258 // validate proxy exception list
259 array
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesExceptionsList
);
264 n
= isA_CFArray(array
) ? CFArrayGetCount(array
) : 0;
265 for (i
= 0; i
< n
; i
++) {
268 str
= CFArrayGetValueAtIndex(array
, i
);
269 if (!isA_CFString(str
) || (CFStringGetLength(str
) == 0)) {
270 // if we don't like the array contents
277 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesExceptionsList
);
281 // validate exclude simple hostnames setting
282 num
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesExcludeSimpleHostnames
);
286 if (!isA_CFNumber(num
) ||
287 !CFNumberGetValue(num
, kCFNumberIntType
, &enabled
)) {
288 // if we don't like the enabled key/value
290 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &enabled
);
291 CFDictionarySetValue(newProxy
,
292 kSCPropNetProxiesExcludeSimpleHostnames
,
298 // cleanup scoped proxies
299 scoped
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesScoped
);
300 if (isA_CFDictionary(scoped
)) {
301 CFMutableDictionaryRef newScoped
;
303 newScoped
= CFDictionaryCreateMutable(NULL
,
305 &kCFTypeDictionaryKeyCallBacks
,
306 &kCFTypeDictionaryValueCallBacks
);
307 CFDictionaryApplyFunction(scoped
,
308 normalize_scoped_proxy
,
310 CFDictionarySetValue(newProxy
, kSCPropNetProxiesScoped
, newScoped
);
311 CFRelease(newScoped
);
314 // cleanup services proxies
315 services
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesServices
);
316 if (isA_CFDictionary(services
)) {
317 CFMutableDictionaryRef newServices
;
319 newServices
= CFDictionaryCreateMutable(NULL
,
321 &kCFTypeDictionaryKeyCallBacks
,
322 &kCFTypeDictionaryValueCallBacks
);
323 CFDictionaryApplyFunction(services
,
324 normalize_services_proxy
,
326 CFDictionarySetValue(newProxy
, kSCPropNetProxiesServices
, newServices
);
327 CFRelease(newServices
);
330 // cleanup split/supplemental proxies
331 supplemental
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesSupplemental
);
332 if (isA_CFArray(supplemental
)) {
333 CFMutableArrayRef newSupplemental
;
335 newSupplemental
= CFArrayCreateMutable(NULL
,
337 &kCFTypeArrayCallBacks
);
338 CFArrayApplyFunction(supplemental
,
339 CFRangeMake(0, CFArrayGetCount(supplemental
)),
340 normalize_supplemental_proxy
,
342 CFDictionarySetValue(newProxy
, kSCPropNetProxiesSupplemental
, newSupplemental
);
343 CFRelease(newSupplemental
);
346 proxy
= CFDictionaryCreateCopy(NULL
,newProxy
);
354 normalize_scoped_proxy(const void *key
, const void *value
, void *context
)
356 CFStringRef interface
= (CFStringRef
)key
;
357 CFDictionaryRef proxy
= (CFDictionaryRef
)value
;
358 CFMutableDictionaryRef newScoped
= (CFMutableDictionaryRef
)context
;
360 proxy
= __SCNetworkProxiesCopyNormalized(proxy
);
361 CFDictionarySetValue(newScoped
, interface
, proxy
);
368 normalize_services_proxy(const void *key
, const void *value
, void *context
)
370 CFNumberRef serviceIndex
= (CFNumberRef
)key
;
371 CFDictionaryRef proxy
= (CFDictionaryRef
)value
;
372 CFMutableDictionaryRef newServices
= (CFMutableDictionaryRef
)context
;
374 proxy
= __SCNetworkProxiesCopyNormalized(proxy
);
375 CFDictionarySetValue(newServices
, serviceIndex
, proxy
);
382 normalize_supplemental_proxy(const void *value
, void *context
)
384 CFDictionaryRef proxy
= (CFDictionaryRef
)value
;
385 CFMutableArrayRef newSupplemental
= (CFMutableArrayRef
)context
;
387 proxy
= __SCNetworkProxiesCopyNormalized(proxy
);
388 CFArrayAppendValue(newSupplemental
, proxy
);
395 SCDynamicStoreCopyProxies(SCDynamicStoreRef store
)
397 return SCDynamicStoreCopyProxiesWithOptions(store
, NULL
);
400 const CFStringRef kSCProxiesNoGlobal
= CFSTR("NO_GLOBAL");
403 SCDynamicStoreCopyProxiesWithOptions(SCDynamicStoreRef store
, CFDictionaryRef options
)
405 Boolean bypass
= FALSE
;
407 CFDictionaryRef proxies
= NULL
;
409 if (options
!= NULL
) {
410 CFBooleanRef bypassGlobalOption
;
412 if (!isA_CFDictionary(options
)) {
413 _SCErrorSet(kSCStatusInvalidArgument
);
417 bypassGlobalOption
= CFDictionaryGetValue(options
, kSCProxiesNoGlobal
);
418 if (isA_CFBoolean(bypassGlobalOption
) && CFBooleanGetValue(bypassGlobalOption
)) {
424 /* copy proxy information from dynamic store */
426 key
= SCDynamicStoreKeyCreateProxies(NULL
);
427 proxies
= SCDynamicStoreCopyValue(store
, key
);
430 if (isA_CFDictionary(proxies
) &&
431 CFDictionaryContainsKey(proxies
, kSCPropNetProxiesBypassAllowed
)) {
432 CFMutableDictionaryRef newProxies
;
434 newProxies
= CFDictionaryCreateMutableCopy(NULL
, 0, proxies
);
438 * Remove kSCPropNetProxiesBypassAllowed property from network
439 * service based configurations.
441 CFDictionaryRemoveValue(newProxies
, kSCPropNetProxiesBypassAllowed
);
442 proxies
= newProxies
;
446 if (proxies
!= NULL
) {
447 CFDictionaryRef base
= proxies
;
449 proxies
= __SCNetworkProxiesCopyNormalized(base
);
452 proxies
= CFDictionaryCreate(NULL
,
456 &kCFTypeDictionaryKeyCallBacks
,
457 &kCFTypeDictionaryValueCallBacks
);
465 _SCNetworkProxiesCopyMatchingInternal(CFDictionaryRef globalConfiguration
,
467 CFStringRef interface
,
468 CFDictionaryRef options
)
470 CFMutableDictionaryRef newProxy
;
472 CFArrayRef proxies
= NULL
;
473 CFDictionaryRef proxy
;
474 int sc_status
= kSCStatusOK
;
475 CFStringRef trimmed
= NULL
;
478 if (!isA_CFDictionary(globalConfiguration
)) {
479 // if no proxy configuration
480 _SCErrorSet(kSCStatusOK
);
484 uuid_clear(match_uuid
);
486 if (isA_CFDictionary(options
)) {
489 interface
= CFDictionaryGetValue(options
, kSCProxiesMatchInterface
);
490 interface
= isA_CFString(interface
);
492 server
= CFDictionaryGetValue(options
, kSCProxiesMatchServer
);
493 server
= isA_CFString(server
);
495 euuid
= CFDictionaryGetValue(options
, kSCProxiesMatchExecutableUUID
);
496 euuid
= isA_CFType(euuid
, CFUUIDGetTypeID());
498 CFUUIDBytes uuid_bytes
= CFUUIDGetUUIDBytes(euuid
);
499 uuid_copy(match_uuid
, (const uint8_t *)&uuid_bytes
);
503 if (interface
!= NULL
) {
504 CFDictionaryRef scoped
;
506 if (!isA_CFString(interface
) ||
507 (CFStringGetLength(interface
) == 0)) {
508 _SCErrorSet(kSCStatusInvalidArgument
);
512 scoped
= CFDictionaryGetValue(globalConfiguration
, kSCPropNetProxiesScoped
);
513 if (scoped
== NULL
) {
514 #if !TARGET_OS_SIMULATOR
515 if (CFDictionaryContainsKey(globalConfiguration
, kSCPropNetProxiesBypassAllowed
) &&
516 ne_session_always_on_vpn_configs_present()) {
518 * The kSCPropNetProxiesBypassAllowed key will be present
519 * for managed proxy configurations where bypassing is *not*
522 * Also (for now), forcing the use of the managed proxy
523 * configurations will only be done with AOVPN present.
527 #endif // !TARGET_OS_SIMULATOR
529 // if no scoped proxy configurations
530 _SCErrorSet(kSCStatusOK
);
534 if (!isA_CFDictionary(scoped
)) {
535 // if corrupt proxy configuration
536 _SCErrorSet(kSCStatusFailed
);
540 proxy
= CFDictionaryGetValue(scoped
, interface
);
542 // if no scoped proxy configuration for this interface
543 _SCErrorSet(kSCStatusOK
);
547 if (!isA_CFDictionary(proxy
)) {
548 // if corrupt proxy configuration
549 _SCErrorSet(kSCStatusFailed
);
553 // return per-interface proxy configuration
554 proxies
= CFArrayCreate(NULL
, (const void **)&proxy
, 1, &kCFTypeArrayCallBacks
);
559 if (server
!= NULL
) {
561 CFMutableArrayRef matching
= NULL
;
564 CFArrayRef supplemental
;
566 trimmed
= _SC_trimDomain(server
);
567 if (trimmed
== NULL
) {
568 _SCErrorSet(kSCStatusInvalidArgument
);
573 server_len
= CFStringGetLength(server
);
575 supplemental
= CFDictionaryGetValue(globalConfiguration
, kSCPropNetProxiesSupplemental
);
576 if (supplemental
!= NULL
) {
577 if (!isA_CFArray(supplemental
)) {
578 // if corrupt proxy configuration
579 sc_status
= kSCStatusFailed
;
583 n
= CFArrayGetCount(supplemental
);
586 for (i
= 0; i
< n
; i
++) {
591 proxy
= CFArrayGetValueAtIndex(supplemental
, i
);
592 if (!isA_CFDictionary(proxy
)) {
593 // if corrupt proxy configuration
597 domain
= CFDictionaryGetValue(proxy
, kSCPropNetProxiesSupplementalMatchDomain
);
598 if (!isA_CFString(domain
)) {
599 // if corrupt proxy configuration
603 domain_len
= CFStringGetLength(domain
);
604 if (domain_len
> 0) {
605 if (!CFStringFindWithOptions(server
,
607 CFRangeMake(0, server_len
),
608 kCFCompareCaseInsensitive
|kCFCompareAnchored
|kCFCompareBackwards
,
610 // if server does not match this proxy domain (or host)
614 if ((server_len
> domain_len
) &&
615 !CFStringFindWithOptions(server
,
617 CFRangeMake(0, server_len
- domain_len
),
618 kCFCompareCaseInsensitive
|kCFCompareAnchored
|kCFCompareBackwards
,
620 // if server does not match this proxy domain
624 // // if this is a "default" (match all) proxy domain
627 if (matching
== NULL
) {
628 matching
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
630 n_matching
= CFArrayGetCount(matching
);
632 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, proxy
);
633 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesSupplementalMatchDomain
);
634 if ((n_matching
== 0) ||
635 !CFArrayContainsValue(matching
, CFRangeMake(0, n_matching
), newProxy
)) {
636 // add this matching proxy
637 CFArrayAppendValue(matching
, newProxy
);
642 if (matching
!= NULL
) {
643 // if we have any supplemental match domains
644 proxies
= CFArrayCreateCopy(NULL
, matching
);
650 // no matches, return "global" proxy configuration
652 #if !TARGET_OS_SIMULATOR
654 #endif // !TARGET_OS_SIMULATOR
656 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, globalConfiguration
);
657 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesScoped
);
658 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesServices
);
659 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesSupplemental
);
660 proxies
= CFArrayCreate(NULL
, (const void **)&newProxy
, 1, &kCFTypeArrayCallBacks
);
665 if (sc_status
!= kSCStatusOK
) {
666 _SCErrorSet(sc_status
);
668 // Note: if we are returning an error then we must
669 // return w/proxies==NULL. At present, there
670 // is no code (above) that would get here with
671 // proxies!=NULL so we don't need to take any
672 // action but future coder's should beware :-)
673 // if (proxies != NULL) {
674 // CFRelease(proxies);
678 if (trimmed
!= NULL
) CFRelease(trimmed
);
684 SCNetworkProxiesCreateProxyAgentData(CFDictionaryRef proxyConfig
)
686 CFDataRef result
= NULL
;
687 CFArrayRef newProxy
= NULL
;
689 if (!isA_CFDictionary(proxyConfig
)) {
690 SC_log(LOG_ERR
, "Invalid proxy configuration");
691 _SCErrorSet(kSCStatusInvalidArgument
);
695 newProxy
= CFArrayCreate(NULL
, (const void **)&proxyConfig
, 1, &kCFTypeArrayCallBacks
);
696 (void)_SCSerialize(newProxy
, &result
, NULL
, NULL
);
703 SCNetworkProxiesCopyMatching(CFDictionaryRef globalConfiguration
,
705 CFStringRef interface
)
707 return _SCNetworkProxiesCopyMatchingInternal(globalConfiguration
, server
, interface
, NULL
);
711 SCNetworkProxiesCopyMatchingWithOptions(CFDictionaryRef globalConfiguration
,
712 CFDictionaryRef options
)
714 return _SCNetworkProxiesCopyMatchingInternal(globalConfiguration
, NULL
, NULL
, options
);