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