1/* 2 * Copyright (c) 2012, 2013 Apple Computer, Inc. All rights reserved. 3 */ 4 5#include <pthread.h> 6#include <string.h> 7#include <stdio.h> 8#include <sys/errno.h> 9#include <sys/types.h> 10#include <sys/socket.h> 11#include <sys/ioctl.h> 12#include <CoreFoundation/CoreFoundation.h> 13#include <SystemConfiguration/SystemConfiguration.h> 14#include <SystemConfiguration/SCPrivate.h> 15#include <mach/boolean.h> 16 17#include "scnc_main.h" 18#include "scnc_utils.h" 19#include "network_detection.h" 20#include "sbslauncher.h" 21#include "app_layer.h" 22 23static boolean_t match_pattern(CFStringRef matchstr, CFStringRef matchpattern); 24static boolean_t match_dns(CFArrayRef cur_dns_array, CFStringRef match_dns_name); 25static boolean_t check_ssid(CFStringRef interface_name, CFArrayRef chk_ssid_array); 26static boolean_t check_dns(CFArrayRef cur_dns_array, CFArrayRef chk_dns_array, int checkall); 27static boolean_t check_domain(CFStringRef cur_domain, CFArrayRef chk_domain_array); 28static boolean_t ondemand_add_action(struct service *serv, CFStringRef action, CFPropertyListRef actionParameters); 29static void ondemand_clear_action(struct service *serv); 30static void dodisconnect(struct service *serv); 31static int start_https_probe(struct service *serv, CFStringRef https_probe_server, CFArrayRef matcharray, CFIndex matcharray_index, 32 CFDictionaryRef primary_dns_dict, CFStringRef primary_interface_name, CFStringRef primary_interface_type); 33static void vpn_action( struct service *serv, CFStringRef match_action, CFPropertyListRef actionParameters); 34static boolean_t resume_check_network(struct service *serv, CFArrayRef match_array, CFIndex match_array_offset, CFDictionaryRef primary_dns_dict, CFStringRef primary_dns_int, CFStringRef primary_interface_type); 35#if TARGET_OS_EMBEDDED 36static boolean_t on_cell_network(); 37#endif 38 39extern TAILQ_HEAD(, service) service_head; 40 41enum { 42 READ = 0, // read end of standard UNIX pipe 43 WRITE = 1 // write end of standard UNIX pipe 44}; 45 46struct probe { 47 struct service *serv; 48 CFStringRef url_string; 49 CFArrayRef matcharray; 50 CFIndex matcharray_index; 51 CFDictionaryRef primary_dns_dict; 52 CFStringRef primary_interface_name; 53 CFStringRef primary_interface_type; 54}; 55 56struct dns_redirect_context { 57 int sbslauncher_sockets[2]; 58 struct service *serv; 59}; 60 61static Boolean 62service_is_valid (struct service *serv) 63{ 64 Boolean found = FALSE; 65 struct service *serv_check = NULL; 66 67 // make sure service is still valid 68 TAILQ_FOREACH(serv_check, &service_head, next) { 69 if (serv_check == serv) { 70 found = TRUE; 71 break; 72 } 73 } 74 75 return found; 76} 77 78#define MAX_SAVED_REDIRECTED_ADDRS 20 79 80static void 81dns_redirect_detection_setup_callback(pid_t pid, void *context) 82{ 83 int *fd = (int*)context; 84 int on = 1; 85 86 fd = ((struct dns_redirect_context*)context)->sbslauncher_sockets; 87 if (pid){ 88 my_close(fd[WRITE]); 89 fd[WRITE] = -1; 90 } else { 91 my_close(fd[READ]); 92 fd[READ] = -1; 93 ioctl(fd[WRITE], FIONBIO, &on); 94 } 95} 96 97static void 98dns_redirect_detection_callback(pid_t pid, int status, struct rusage *rusage, void *ctx) 99{ 100 struct dns_redirect_context *context = (struct dns_redirect_context*)ctx; 101 struct service *serv = context->serv; 102 int exitcode; 103 uint8_t buf[(sizeof(struct sockaddr_storage) * MAX_SAVED_REDIRECTED_ADDRS)]; 104 105 if (serv == NULL || !service_is_valid(serv)) { 106 goto done; 107 } 108 109 exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : -1; 110 111 Boolean dnsRedirectDetected = (exitcode > 0); 112 113 my_CFRelease(&serv->dnsRedirectedAddresses); 114 115 if (dnsRedirectDetected) { 116 struct sockaddr *addr = NULL; 117 int i; 118 int readlen = 0; 119 int validatedBytes = 0; 120 121 if ((readlen = read(context->sbslauncher_sockets[READ], buf, sizeof(buf))) > 0) { 122 CFMutableDataRef ipv4Data = CFDataCreateMutable(kCFAllocatorDefault, 0); 123 CFMutableDataRef ipv6Data = CFDataCreateMutable(kCFAllocatorDefault, 0); 124 if (ipv4Data && ipv6Data) { 125 for (i = 0, addr = (void*)buf; 126 (i < MAX_SAVED_REDIRECTED_ADDRS) && (addr->sa_len <= (readlen - validatedBytes)); 127 i++) { 128 if (addr->sa_family == AF_INET) { 129 struct sockaddr_in *addr_in = (struct sockaddr_in *)addr; 130 if (addr_in->sin_len < sizeof(struct sockaddr_in)) { 131 break; 132 } 133 CFDataAppendBytes(ipv4Data, (uint8_t*)&addr_in->sin_addr, sizeof(struct in_addr)); 134 } else if (addr->sa_family == AF_INET6) { 135 struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr; 136 if (addr_in6->sin6_len < sizeof(struct sockaddr_in6)) { 137 break; 138 } 139 CFDataAppendBytes(ipv6Data, (uint8_t*)&addr_in6->sin6_addr, sizeof(struct in6_addr)); 140 } 141 142 validatedBytes += addr->sa_len; 143 addr = (struct sockaddr *)(((uint8_t*)addr) + addr->sa_len); 144 } 145 146 CFMutableDictionaryRef addressesDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 147 if (addressesDict) { 148 if (CFDataGetLength(ipv4Data)) { 149 CFMutableDictionaryRef ipv4Dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 150 if (ipv4Dict) { 151 CFDictionaryAddValue(ipv4Dict, kSCNetworkConnectionNetworkInfoAddresses, ipv4Data); 152 CFDictionaryAddValue(addressesDict, kSCNetworkConnectionNetworkInfoIPv4, ipv4Dict); 153 my_CFRelease(&ipv4Dict); 154 } 155 } 156 if (CFDataGetLength(ipv6Data)) { 157 CFMutableDictionaryRef ipv6Dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 158 if (ipv6Dict) { 159 CFDictionaryAddValue(ipv6Dict, kSCNetworkConnectionNetworkInfoAddresses, ipv6Data); 160 CFDictionaryAddValue(addressesDict, kSCNetworkConnectionNetworkInfoIPv6, ipv6Dict); 161 my_CFRelease(&ipv6Dict); 162 } 163 } 164 serv->dnsRedirectedAddresses = addressesDict; 165 } 166 } 167 my_CFRelease(&ipv4Data); 168 my_CFRelease(&ipv6Data); 169 } 170 } 171 172 SCLog(TRUE, LOG_DEBUG, CFSTR("dns_redirect_detection_callback %d %d %d"), status, exitcode, dnsRedirectDetected); 173 if (serv->dnsRedirectDetected != dnsRedirectDetected) { 174 serv->dnsRedirectDetected = dnsRedirectDetected; 175 ondemand_add_service(serv, FALSE); 176 } 177 178done: 179 my_close(context->sbslauncher_sockets[READ]); 180 my_close(context->sbslauncher_sockets[WRITE]); 181 free(context); 182} 183 184static Boolean 185dns_redirect_detection_start(struct service *serv) 186{ 187 struct dns_redirect_context *context; 188 char socketstr[10]; 189 190 context = malloc(sizeof(struct dns_redirect_context)); 191 if (context == NULL) 192 goto fail; 193 194 if ((socketpair(AF_LOCAL, SOCK_STREAM, 0, context->sbslauncher_sockets) == -1)){ 195 SCLog(TRUE, LOG_ERR, CFSTR("SCNC Controller: socketpair fails, errno = %s"), strerror(errno)); 196 goto fail; 197 } 198 snprintf(socketstr, sizeof(socketstr), "%d", context->sbslauncher_sockets[WRITE]); 199 200 context->serv = serv; 201 202 if (SCNCExecSBSLauncherCommandWithArguments(SBSLAUNCHER_TYPE_DETECT_DNS_REDIRECT, dns_redirect_detection_setup_callback, dns_redirect_detection_callback, (void*)context, socketstr, NULL)) { 203 return TRUE; 204 } 205 206fail: 207 if (context != NULL) { 208 my_close(context->sbslauncher_sockets[READ]); 209 my_close(context->sbslauncher_sockets[WRITE]); 210 free(context); 211 } 212 return FALSE; 213} 214 215/* ----------------------------------------------------------------------------- 216 ----------------------------------------------------------------------------- */ 217static boolean_t 218match_pattern(CFStringRef matchstr, CFStringRef matchpattern) 219{ 220 CFRange range; 221 Boolean ret = FALSE; 222 CFStringRef s1 = NULL; 223 Boolean s1_created = FALSE; 224 225 if (CFStringHasSuffix(matchpattern, CFSTR("*"))){ 226 range.location = 0; 227 range.length = CFStringGetLength(matchpattern) - 1; 228 s1 = CFStringCreateWithSubstring(NULL, matchpattern, range); 229 if (s1 == NULL) { 230 goto done; 231 } 232 s1_created = TRUE; 233 ret = CFStringHasPrefix(matchstr, s1); 234 } else { 235 ret = CFEqual(matchstr, matchpattern); 236 } 237 238done: 239 if (s1_created) CFRelease(s1); 240 return ret; 241} 242 243/* ----------------------------------------------------------------------------- 244 ----------------------------------------------------------------------------- */ 245static boolean_t 246match_dns(CFArrayRef cur_dns_array, CFStringRef match_dns_name) 247{ 248 CFStringRef cur_dns = NULL; 249 boolean_t match = false; 250 int count = 0, i; 251 252 count = CFArrayGetCount(cur_dns_array); 253 for (i = 0; i < count; i++) { 254 cur_dns = CFArrayGetValueAtIndex(cur_dns_array, i); 255 256 if (match_pattern(match_dns_name, cur_dns)){ 257 match = true; 258 break; 259 } 260 } 261 return match; 262} 263 264/* ----------------------------------------------------------------------------- 265 ----------------------------------------------------------------------------- */ 266static boolean_t 267check_ssid(CFStringRef interface_name, CFArrayRef chk_ssid_array) 268{ 269 CFDictionaryRef interface_dict = NULL; 270 CFStringRef interface_key = NULL; 271 CFStringRef match_ssid = NULL; 272 boolean_t match = false; 273 274 if (!isArray(chk_ssid_array) || !isString(interface_name)) { 275 goto done; 276 } 277 278 interface_key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 279 kSCDynamicStoreDomainState, 280 interface_name, 281 kSCEntNetAirPort); 282 283 interface_dict = SCDynamicStoreCopyValue(gDynamicStore, interface_key); 284 285 if (!isDictionary(interface_dict)) { 286 goto done; 287 } 288 289 match_ssid = CFDictionaryGetValue(interface_dict, SC_AIRPORT_SSID_STR_KEY); 290 if (isString(match_ssid) && CFArrayContainsValue(chk_ssid_array, CFRangeMake(0, CFArrayGetCount(chk_ssid_array)), match_ssid)) { 291 match = true; 292 } 293 294done: 295 if (interface_key) { 296 CFRelease(interface_key); 297 } 298 if (interface_dict) { 299 CFRelease(interface_dict); 300 } 301 302 return match; 303} 304 305/* ----------------------------------------------------------------------------- 306 ----------------------------------------------------------------------------- */ 307static boolean_t 308check_dns(CFArrayRef cur_dns_array, CFArrayRef chk_dns_array, int checkall) 309{ 310 CFStringRef match_dns_name = NULL; 311 int count = 0, i; 312 int localDNScount = 0; 313 boolean_t match = false; 314 315 count = CFArrayGetCount(chk_dns_array); 316 localDNScount = CFArrayGetCount(cur_dns_array); 317 318 for (i = 0; i < localDNScount; i++) { 319 match_dns_name = CFArrayGetValueAtIndex(cur_dns_array, i); 320 if (!isString(match_dns_name)) 321 break; 322 323 /* check domain name */ 324 if (match_dns(chk_dns_array, match_dns_name)){ 325 if (!checkall){ 326 /* we've found a match, get out */ 327 match = true; 328 break; 329 } 330 continue; 331 }else { 332 break; 333 } 334 } 335 if ( i >= localDNScount) 336 match = true; 337 338 return match; 339} 340 341/* ----------------------------------------------------------------------------- 342 ----------------------------------------------------------------------------- */ 343 344static boolean_t 345check_domain(CFStringRef cur_domain, CFArrayRef chk_domain_array) 346{ 347 CFStringRef match_domain_name = NULL; 348 int count = 0, i; 349 boolean_t match = false; 350 351 count = CFArrayGetCount(chk_domain_array); 352 353 for (i = 0; i < count; i++) { 354 match_domain_name = CFArrayGetValueAtIndex(chk_domain_array, i); 355 if (match_domain_name){ 356 if (_SC_domainEndsWithDomain(cur_domain, match_domain_name)){ 357 // if (CFStringCompare(cur_domain, match_domain_name, 0) == kCFCompareEqualTo) { 358 /* we've found a match, get out */ 359 match = true; 360 break; 361 } 362 } 363 } 364 return match; 365} 366 367/* ----------------------------------------------------------------------------- 368 ----------------------------------------------------------------------------- */ 369int copy_trigger_info(struct service *serv, CFMutableDictionaryRef *ondemand_dict_cp, 370 CFMutableArrayRef *trigger_array_cp, CFMutableDictionaryRef *trigger_dict_cp) 371{ 372 CFStringRef serviceid = NULL; 373 CFDictionaryRef ondemand_dict = NULL, current_trigger_dict = NULL; 374 CFArrayRef current_triggers_array = NULL; 375 int count = 0, i, found = 0; 376 int ret = -1; 377 378 *ondemand_dict_cp = NULL; 379 *trigger_array_cp = NULL; 380 *trigger_dict_cp = NULL; 381 382 if (gOndemand_key == NULL) 383 goto fail; 384 385 ondemand_dict = SCDynamicStoreCopyValue(gDynamicStore, gOndemand_key); 386 if (ondemand_dict) { 387 current_triggers_array = CFDictionaryGetValue(ondemand_dict, kSCNetworkConnectionOnDemandTriggers); 388 if (isArray(current_triggers_array)) { 389 found = 0; 390 count = CFArrayGetCount(current_triggers_array); 391 for (i = 0; i < count; i++) { 392 current_trigger_dict = CFArrayGetValueAtIndex(current_triggers_array, i); 393 if (!isDictionary(current_trigger_dict)) 394 continue; 395 serviceid = CFDictionaryGetValue(current_trigger_dict, kSCNetworkConnectionOnDemandServiceID); 396 if (!isString(serviceid)) 397 continue; 398 if (CFStringCompare(serviceid, serv->serviceID, 0) == kCFCompareEqualTo) { 399 found = 1; 400 ret = i; 401 break; 402 } 403 } 404 } 405 } 406 407 if (!found){ 408 goto fail; 409 } 410 411 /* make a copy of current_trigger_dict */ 412 *trigger_dict_cp = CFDictionaryCreateMutableCopy(0, 0, current_trigger_dict); 413 if (*trigger_dict_cp == NULL) 414 goto fail; 415 416 *ondemand_dict_cp = CFDictionaryCreateMutableCopy(0, 0, ondemand_dict); 417 if (*ondemand_dict_cp == NULL) 418 goto fail; 419 420 *trigger_array_cp = CFArrayCreateMutableCopy(0, 0, current_triggers_array); 421 if (*trigger_array_cp == NULL) 422 goto fail; 423 424 goto done; 425 426fail: 427 my_CFRelease(trigger_dict_cp); 428 my_CFRelease(ondemand_dict_cp); 429 my_CFRelease(trigger_array_cp); 430 431done: 432 my_CFRelease(&ondemand_dict); 433 434 return ret; 435 436} 437 438/* ----------------------------------------------------------------------------- 439 ----------------------------------------------------------------------------- */ 440static boolean_t 441ondemand_add_action(struct service *serv, CFStringRef action, CFPropertyListRef actionParameters) 442{ 443 boolean_t changed = !my_CFEqual(serv->ondemandAction, action); 444 my_CFRelease(&serv->ondemandAction); 445 my_CFRelease(&serv->ondemandActionParameters); 446 serv->ondemandAction = my_CFRetain(action); 447 if (isA_CFPropertyList(actionParameters)) { 448 serv->ondemandActionParameters = my_CFRetain(actionParameters); 449 } 450 return changed; 451} 452 453/* ----------------------------------------------------------------------------- 454 ----------------------------------------------------------------------------- */ 455static 456void ondemand_clear_action(struct service *serv) 457{ 458 my_CFRelease(&serv->ondemandAction); 459} 460 461/* ----------------------------------------------------------------------------- 462 ----------------------------------------------------------------------------- */ 463static 464void dodisconnect(struct service *serv) 465{ 466 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC Controller: do disconnect by network detection")); 467 468 /* reset ignore vpn flag */ 469 scnc_stop(serv, 0, SIGTERM, SCNC_STOP_USER_REQ); 470} 471 472static 473void ondemand_save_probe_result(struct service *serv, CFStringRef URL, Boolean succeeded) 474{ 475 if (serv->ondemandProbeResults == NULL) { 476 serv->ondemandProbeResults = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 477 } 478 479 CFMutableDictionaryRef results = (CFMutableDictionaryRef)serv->ondemandProbeResults; 480 481 CFDictionarySetValue(results, URL, succeeded ? kCFBooleanTrue : kCFBooleanFalse); 482} 483 484static 485Boolean ondemand_probe_already_sent(struct service *serv, CFStringRef URL) 486{ 487 if (serv->ondemandProbeResults != NULL && URL != NULL) { 488 return CFDictionaryContainsValue(serv->ondemandProbeResults, URL); 489 } 490 return FALSE; 491} 492 493static 494void ondemand_clear_probe_results(struct service *serv) 495{ 496 my_CFRelease(&serv->ondemandProbeResults); 497} 498 499static 500Boolean doEvaluateConnection(struct service *serv, CFArrayRef domainRules) 501{ 502 Boolean needServerExceptionDNSConfig = FALSE; 503 CFMutableDictionaryRef DNSTriggeringDicts = NULL; 504 CFIndex domainRulesCount = 0; 505 CFIndex i; 506 507 if (!isArray(domainRules)) { 508 return FALSE; 509 } 510 511 domainRulesCount = CFArrayGetCount(domainRules); 512 if (domainRulesCount == 0) { 513 return FALSE; 514 } 515 516 DNSTriggeringDicts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 517 518 if (!DNSTriggeringDicts) { 519 return FALSE; 520 } 521 522 for (i = 0; i < domainRulesCount; i++) { 523 CFDictionaryRef domainRule = CFArrayGetValueAtIndex(domainRules, i); 524 if (!isDictionary(domainRule)) { 525 continue; 526 } 527 528 CFStringRef domainAction = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomainAction); 529 if (!isString(domainAction)) { 530 continue; 531 } 532 533 if (CFStringCompare(domainAction, kSCValNetVPNOnDemandRuleActionParametersDomainActionConnectIfNeeded, 0) == kCFCompareEqualTo) { 534 CFArrayRef dnsServers = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersRequiredDNSServers); 535 CFArrayRef domains = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomains); 536 if (isArray(dnsServers) && CFArrayGetCount(dnsServers) && isArray(domains) && CFArrayGetCount(domains)) { 537 CFDictionaryRef dnsDictionary = NULL; 538 CFStringRef serviceExt = NULL; 539 CFStringRef key = NULL; 540 CFMutableArrayRef mutableDomains = NULL; 541 CFIndex domainCount = CFArrayGetCount(domains); 542 CFIndex domainIndex = 0; 543 544 mutableDomains = CFArrayCreateMutableCopy(kCFAllocatorDefault, domainCount, domains); 545 if (mutableDomains) { 546 /* Sanitize domain array for *.example domains */ 547 for (domainIndex = 0; domainIndex < domainCount; domainIndex++) { 548 CFStringRef domain = CFArrayGetValueAtIndex(mutableDomains, domainIndex); 549 if (CFStringHasPrefix(domain, CFSTR("*."))) { 550 CFStringRef newDomain = NULL; 551 CFRange range; 552 range.location = 2; 553 range.length = CFStringGetLength(domain) - 2; 554 if (range.length > 0) { 555 newDomain = CFStringCreateWithSubstring(kCFAllocatorDefault, domain, range); 556 if (newDomain) { 557 CFArraySetValueAtIndex(mutableDomains, domainIndex, newDomain); 558 CFRelease(newDomain); 559 } 560 } 561 } 562 } 563 564 /* Create temporary service key */ 565 serviceExt = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("%@-TMP%ld"), serv->serviceID, i); 566 if (serviceExt) { 567 dnsDictionary = create_dns(gDynamicStore, serv->serviceID, dnsServers, NULL, mutableDomains, TRUE); 568 key = SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, serviceExt, kSCEntNetDNS); 569 needServerExceptionDNSConfig = TRUE; 570 } 571 } 572 if (isDictionary(dnsDictionary) && isString(key)) { 573 CFDictionaryAddValue(DNSTriggeringDicts, key, dnsDictionary); 574 } 575 my_CFRelease(&serviceExt); 576 my_CFRelease(&mutableDomains); 577 my_CFRelease(&key); 578 my_CFRelease(&dnsDictionary); 579 } 580 581 CFStringRef probeURL = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe); 582 if (isString(probeURL) && !ondemand_probe_already_sent(serv, probeURL)) { 583 CFStringRef primaryInterface = copy_primary_interface_name(serv->serviceID); 584 start_https_probe(serv, probeURL, NULL, 0, NULL, primaryInterface, NULL); 585 my_CFRelease(&primaryInterface); 586 } 587 } 588 } 589 590 if (needServerExceptionDNSConfig) 591 { 592 Boolean isHostname = FALSE; 593 CFStringRef remoteAddress = scnc_copy_remote_server(serv, &isHostname); 594 if (isHostname && isA_CFString(remoteAddress) && CFStringGetLength(remoteAddress) > 0) { 595 CFStringRef serviceExt = NULL; 596 CFArrayRef domains = CFArrayCreate(kCFAllocatorDefault, (const void **)&remoteAddress, 1, &kCFTypeArrayCallBacks); 597 CFStringRef dnsstatekey = CREATEGLOBALSTATE(kSCEntNetDNS); 598 CFDictionaryRef globalDNS = dnsstatekey ? SCDynamicStoreCopyValue(gDynamicStore, dnsstatekey) : NULL; 599 CFArrayRef dnsServers = globalDNS ? CFDictionaryGetValue(globalDNS, kSCPropNetDNSServerAddresses) : NULL; 600 601 serviceExt = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("%@-TMP-SERVER"), serv->serviceID); 602 if (serviceExt) { 603 CFDictionaryRef dnsDictionary = create_dns(gDynamicStore, serv->serviceID, dnsServers, NULL, domains, TRUE); 604 CFStringRef key = SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, serviceExt, kSCEntNetDNS); 605 if (isDictionary(dnsDictionary) && isString(key)) { 606 CFDictionaryAddValue(DNSTriggeringDicts, key, dnsDictionary); 607 } 608 my_CFRelease(&dnsDictionary); 609 my_CFRelease(&key); 610 } 611 612 my_CFRelease(&domains); 613 my_CFRelease(&globalDNS); 614 my_CFRelease(&dnsstatekey); 615 my_CFRelease(&serviceExt); 616 } 617 my_CFRelease(&remoteAddress); 618 } 619 620 if (CFDictionaryGetCount(DNSTriggeringDicts) == 0) { 621 my_CFRelease(&DNSTriggeringDicts); 622 return FALSE; 623 } 624 625 /* Add supplemental match domains */ 626 627 if (serv->ondemandDNSTriggeringDicts && DNSTriggeringDicts && CFEqual(serv->ondemandDNSTriggeringDicts, DNSTriggeringDicts)) { 628 /* If equal, we don't need to do anything */ 629 my_CFRelease(&DNSTriggeringDicts); 630 } else { 631 /* Otherwise, uninstall the old dictionaries from the dynamic store */ 632 ondemand_unpublish_dns_triggering_dicts(serv); 633 my_CFRelease(&serv->ondemandDNSTriggeringDicts); 634 serv->ondemandDNSTriggeringDicts = DNSTriggeringDicts; 635 } 636 637 return TRUE; 638} 639 640/* ----------------------------------------------------------------------------- 641 ----------------------------------------------------------------------------- */ 642static 643void vpn_action(struct service *serv, CFStringRef match_action, CFPropertyListRef actionParameters) 644{ 645 Boolean checked_dns_redirect = FALSE; 646 Boolean set_dns_triggering_dicts = FALSE; 647 boolean_t changed; 648 649 changed = ondemand_add_action(serv, match_action, actionParameters); 650 651 /* what VPN action should take place */ 652 if (isString(match_action)){ 653 if (CFStringCompare(match_action, kSCValNetVPNOnDemandRuleActionDisconnect, 0) == kCFCompareEqualTo){ 654 dodisconnect(serv); 655 } else if (CFStringCompare(match_action, kSCValNetVPNOnDemandRuleActionAllow, 0) == kCFCompareEqualTo) { 656 /* For allow, if there is a retry list, detect redirecting */ 657 CFArrayRef onRetryArray = NULL; 658 onRetryArray = CFDictionaryGetValue(serv->systemprefs, kSCPropNetVPNOnDemandMatchDomainsOnRetry); 659 if (isArray(onRetryArray) && (CFArrayGetCount(onRetryArray) > 0)) { 660 checked_dns_redirect = dns_redirect_detection_start(serv); 661 } 662 } else if (isArray(actionParameters) && CFStringCompare(match_action, kSCValNetVPNOnDemandRuleActionEvaluateConnection, 0) == kCFCompareEqualTo) { 663 /* Always detect DNS redirection if evaluating connection */ 664 checked_dns_redirect = dns_redirect_detection_start(serv); 665 666 /* Create DNS triggering dicts */ 667 set_dns_triggering_dicts = doEvaluateConnection(serv, actionParameters); 668 } 669 670 /* Do nothing for ignore and connect */ 671 } 672 673 if (!checked_dns_redirect) { 674 serv->dnsRedirectDetected = FALSE; 675 } 676 677 if (!set_dns_triggering_dicts && serv->ondemandDNSTriggeringDicts) { 678 ondemand_unpublish_dns_triggering_dicts(serv); 679 my_CFRelease(&serv->ondemandDNSTriggeringDicts); 680 } 681 682 if (serv->flags & FLAG_SETUP_ONDEMAND) { 683 ondemand_add_service(serv, FALSE); 684 } 685 686 if (changed) { 687 app_layer_handle_network_detection_change(serv->serviceID); 688 } 689} 690 691/* ----------------------------------------------------------------------------- 692 ----------------------------------------------------------------------------- */ 693static void cancel_https_probe(struct probe *probeptr) 694{ 695 my_CFRelease(&probeptr->url_string); 696 my_CFRelease(&probeptr->matcharray); 697 my_CFRelease(&probeptr->primary_interface_name); 698 my_CFRelease(&probeptr->primary_interface_type); 699 my_CFRelease(&probeptr->primary_dns_dict); 700 free(probeptr); 701} 702 703/* ----------------------------------------------------------------------------- 704 ----------------------------------------------------------------------------- */ 705static 706void https_probe_callback(pid_t pid, int status, struct rusage *rusage, void *context) 707{ 708 struct probe *probeptr = (struct probe*)context; 709 struct service *serv; 710 CFStringRef match_action = NULL; 711 CFDictionaryRef match_dict = NULL; 712 int exitcode; 713 714 if (probeptr == NULL) { 715 return; 716 } 717 718 serv = probeptr->serv; 719 if (serv == NULL || !service_is_valid(serv)) { 720 /* If our service is no longer valid, bail */ 721 cancel_https_probe(probeptr); 722 return; 723 } 724 725 exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : -1; 726 727 ondemand_save_probe_result(serv, probeptr->url_string, (exitcode > 0)); 728 729 if (probeptr->matcharray) { 730 if (exitcode > 0){ 731 /* If probe worked, do the action */ 732 match_dict = CFArrayGetValueAtIndex(probeptr->matcharray, probeptr->matcharray_index); 733 if (isDictionary(match_dict)) { 734 match_action = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleAction); 735 if (isString(match_action)){ 736 vpn_action(serv, match_action, CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleActionParameters)); 737 } 738 } 739 } else { 740 /* Try next action */ 741 resume_check_network(serv, probeptr->matcharray, probeptr->matcharray_index + 1, probeptr->primary_dns_dict, probeptr->primary_interface_name, probeptr->primary_interface_type); 742 } 743 } else { 744 /* If the probe is outside of normal network detection, make sure we publish the result */ 745 if (serv->flags & FLAG_SETUP_ONDEMAND) { 746 ondemand_add_service(serv, FALSE); 747 } 748 } 749 750 /* Release structures */ 751 cancel_https_probe(probeptr); 752} 753 754/* ----------------------------------------------------------------------------- 755 ----------------------------------------------------------------------------- */ 756static 757struct probe* alloc_new_probe(struct service *serv, CFStringRef url_string, CFArrayRef matcharray, CFIndex matcharray_index, 758 CFDictionaryRef primary_dns_dict, CFStringRef primary_interface_name, 759 CFStringRef primary_interface_type) 760{ 761 struct probe *probeptr = NULL; 762 763 /* set up probe ref */ 764 if (!(probeptr = malloc(sizeof(struct probe)))) 765 return NULL; 766 767 probeptr->serv = serv; 768 probeptr->url_string = my_CFRetain(url_string); 769 probeptr->matcharray = my_CFRetain(matcharray); 770 probeptr->matcharray_index = matcharray_index; 771 probeptr->primary_dns_dict = my_CFRetain(primary_dns_dict); 772 probeptr->primary_interface_name = my_CFRetain(primary_interface_name); 773 probeptr->primary_interface_type = my_CFRetain(primary_interface_type);; 774 775 return probeptr; 776} 777 778/* ----------------------------------------------------------------------------- 779 returns:- 780 1 - child was spawned without error 781 ----------------------------------------------------------------------------- */ 782static 783int start_https_probe(struct service *serv, CFStringRef https_probe_server, CFArrayRef matcharray, CFIndex matcharray_index, 784 CFDictionaryRef primary_dns_dict, CFStringRef primary_interface_name, CFStringRef primary_interface_type) 785{ 786 struct probe *probeptr; 787 char https_probe_server_str[255]; 788 char interface_str[IFNAMSIZ]; 789 int ret = 0; 790 791 if (primary_interface_name == NULL || https_probe_server == NULL){ 792 goto done; 793 } 794 795 if (!CFStringGetCString(https_probe_server, https_probe_server_str, sizeof(https_probe_server_str), kCFStringEncodingMacRoman)) { 796 goto done; 797 } 798 799 if (!CFStringGetCString(primary_interface_name, interface_str, sizeof(interface_str), kCFStringEncodingMacRoman)) { 800 goto done; 801 } 802 803 if ((probeptr = alloc_new_probe(serv, https_probe_server, matcharray, matcharray_index, primary_dns_dict, primary_interface_name, primary_interface_type)) == NULL) { 804 goto done; 805 } 806 807 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC Controller: launching sbslauncher for %s (via %s)"), https_probe_server_str, interface_str); 808 809 if (SCNCExecSBSLauncherCommandWithArguments(SBSLAUNCHER_TYPE_PROBE_SERVER, NULL, https_probe_callback, (void*)probeptr, https_probe_server_str, interface_str, NULL) == 0) { 810 cancel_https_probe(probeptr); 811 goto done; 812 } 813 814 /* Save the result as FALSE until we hear a reply */ 815 ondemand_save_probe_result(serv, https_probe_server, FALSE); 816 ret = 1; 817 818done: 819 return ret; 820} 821 822static boolean_t 823resume_check_network(struct service *serv, CFArrayRef match_array, CFIndex match_array_offset, CFDictionaryRef primary_dns_dict, CFStringRef primary_interface_name, CFStringRef primary_interface_type) 824{ 825 CFStringRef match_action = NULL; 826 CFDictionaryRef match_dict = NULL; 827 CFStringRef cur_domain = NULL; 828 CFArrayRef cur_dns = NULL; 829 CFArrayRef match_domain_array = NULL; 830 CFArrayRef match_dns_array = NULL; 831 CFArrayRef match_ssid_array = NULL; 832 CFStringRef match_interface = NULL; 833 CFStringRef probe_server = NULL; 834 boolean_t found = false; 835 boolean_t domain_matched = true; 836 boolean_t dns_matched = true; 837 boolean_t ssid_matched = true; 838 boolean_t interface_matched = true; 839 boolean_t probe_sent = false; 840 CFIndex count = 0, i; 841 842 if (!isArray(match_array)) { 843 goto done; 844 } 845 846 count = CFArrayGetCount(match_array); 847 if (match_array_offset >= count) { 848 goto done; 849 } 850 851 if (primary_dns_dict == NULL || primary_interface_name == NULL || primary_interface_type == NULL) { 852 goto done; 853 } 854 855 cur_domain = CFDictionaryGetValue(primary_dns_dict, kSCPropNetDNSDomainName); 856 cur_dns = CFDictionaryGetValue(primary_dns_dict, kSCPropNetDNSServerAddresses); 857 858 /* Traverse array, starting at offset */ 859 for (i = match_array_offset; i < count; i++) { 860 match_dict = (CFDictionaryRef)CFArrayGetValueAtIndex(match_array, i); 861 if (!(isDictionary(match_dict))) 862 continue; 863 864 /* check interface */ 865 match_interface = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleInterfaceTypeMatch); 866 if (match_interface == NULL 867 || (isString(match_interface) && my_CFEqual(match_interface, primary_interface_type))) { 868 interface_matched = true; 869 } else { 870 interface_matched = false; 871 } 872 873 if (!interface_matched) 874 continue; 875 876 /* check ssid */ 877 match_ssid_array = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleSSIDMatch); 878 if (isArray(match_ssid_array)) { 879 ssid_matched = check_ssid(primary_interface_name, match_ssid_array); 880 } else if (match_ssid_array == NULL) { 881 ssid_matched = true; 882 } 883 884 if (!ssid_matched) 885 continue; 886 887 /* check domain name */ 888 match_domain_array = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleDNSDomainMatch); 889 if (isArray(match_domain_array) && isString(cur_domain)){ 890 domain_matched = check_domain(cur_domain, match_domain_array); 891 } else if (match_domain_array == NULL) { 892 domain_matched = true; 893 } else { 894 domain_matched = false; 895 } 896 897 if ( !domain_matched ) /* domain name does not match, continue to next */ 898 continue; 899 900 /* check dns */ 901 match_dns_array = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleDNSServerAddressMatch); 902 if (isArray(match_dns_array)){ 903 dns_matched = check_dns(cur_dns, match_dns_array, 1); 904 } else if (match_dns_array == NULL){ 905 dns_matched = true; 906 } 907 908 if (!dns_matched) 909 continue; 910 911 /* send probe if specified */ 912 probe_server = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleURLStringProbe); 913 if (isString(probe_server) && primary_interface_name){ 914 if (!start_https_probe(serv, probe_server, match_array, i, primary_dns_dict, primary_interface_name, primary_interface_type)){ 915 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC_Controller:check_network -- start_https_probe failed")); 916 continue; /* If we fail the probe, check the next rule. */ 917 } 918 probe_sent = true; 919 } 920 break; /* found a match */ 921 } 922 923 // Start of done section: 924 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC Controller:check_network -- interface_matched %d, ssid_matched %d, domain_matched %d, dns_matched %d"), interface_matched, ssid_matched, domain_matched, dns_matched); 925 926 if (!probe_sent){ 927 if (isDictionary(match_dict)){ 928 found = interface_matched && ssid_matched && domain_matched && dns_matched; 929 if (found){ 930 /* get the action for the found network or default no match fall back action */ 931 match_action = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleAction); 932 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC Controller:check_network - match_action %@"), match_action); 933 vpn_action(serv, match_action, CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleActionParameters)); 934 } 935 }else 936 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC Controller:check_network -- no match_dict")); 937 938 } 939 940done: 941 return found; 942} 943 944/* ----------------------------------------------------------------------------- 945 do network detection 946 ----------------------------------------------------------------------------- */ 947boolean_t check_network(struct service *serv) 948{ 949 CFDictionaryRef primary_dns_dict = NULL; 950 CFStringRef primary_interface_name = NULL; 951 CFStringRef primary_interface_type = NULL; 952 CFStringRef primary_interface_service_id = NULL; 953 954 Boolean did_network_detection = FALSE; 955 Boolean result = FALSE; 956 957 /* Always clear actions */ 958 if (serv->flags & FLAG_SETUP_ONDEMAND) { 959 /* clear action from trigger */ 960 ondemand_clear_action(serv); 961 } 962 963 /* Always clear probe results */ 964 ondemand_clear_probe_results(serv); 965 966 /* Make sure there is a primary interface */ 967 primary_interface_name = copy_primary_interface_name(serv->serviceID); 968 if (!isString(primary_interface_name)) { 969 goto done; 970 } 971 972 /* Make sure there is a valid interface type and DNS dictionary */ 973 primary_interface_service_id = copy_service_id_for_interface(primary_interface_name); 974 if (!isString(primary_interface_service_id)) { 975 goto done; 976 } 977 978 primary_interface_type = copy_interface_type(primary_interface_service_id); 979 if (!isString(primary_interface_type)) { 980 goto done; 981 } 982 983 primary_dns_dict = copy_dns_dict(primary_interface_service_id); 984 if (!isDictionary(primary_dns_dict)) { 985 goto done; 986 } 987 988 /* Now actually check the network, potentially sending out a probe */ 989 result = resume_check_network(serv, CFDictionaryGetValue(serv->systemprefs, kSCPropNetVPNOnDemandRules), 0, primary_dns_dict, primary_interface_name, primary_interface_type); 990 did_network_detection = TRUE; 991 992done: 993 if (!did_network_detection) { 994 /* Notify listeners about the clear */ 995 if (serv->flags & FLAG_SETUP_ONDEMAND) { 996 ondemand_add_service(serv, FALSE); 997 } 998 } 999 1000 my_CFRelease(&primary_interface_service_id); 1001 my_CFRelease(&primary_interface_name); 1002 my_CFRelease(&primary_interface_type); 1003 my_CFRelease(&primary_dns_dict); 1004 return result; 1005} 1006