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
42CFStringRef
43SCDynamicStoreKeyCreateProxies(CFAllocatorRef allocator)
44{
45	return SCDynamicStoreKeyCreateNetworkGlobalEntity(allocator,
46							  kSCDynamicStoreDomainState,
47							  kSCEntNetProxies);
48}
49
50
51static void
52validate_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
138static void
139normalize_scoped_proxy(const void *key, const void *value, void *context);
140
141
142static void
143normalize_services_proxy(const void *key, const void *value, void *context);
144
145
146static void
147normalize_supplemental_proxy(const void *value, void *context);
148
149
150static 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
352static void
353normalize_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
366static void
367normalize_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
380static void
381normalize_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
393CFDictionaryRef
394SCDynamicStoreCopyProxies(SCDynamicStoreRef store)
395{
396	return SCDynamicStoreCopyProxiesWithOptions(store, NULL);
397}
398
399const CFStringRef	kSCProxiesNoGlobal	= CFSTR("NO_GLOBAL");
400
401CFDictionaryRef
402SCDynamicStoreCopyProxiesWithOptions(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
449CFArrayRef
450SCNetworkProxiesCopyMatching(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}
665