this repo has no description
at fixPythonPipStalling 4808 lines 145 kB view raw
1/* 2 * Copyright (c) 2003-2019 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 * April 14, 2004 Christophe Allie <callie@apple.com> 28 * - use mach messages 29 30 * December 20, 2002 Christophe Allie <callie@apple.com> 31 * - initial revision 32 */ 33 34 35//#define DEBUG_MACH_PORT_ALLOCATIONS 36 37 38#include <TargetConditionals.h> 39#include <sys/cdefs.h> 40#include <dispatch/dispatch.h> 41#include <CoreFoundation/CFXPCBridge.h> 42 43#include "SCNetworkConnectionInternal.h" 44#include "SCNetworkConfigurationInternal.h" 45#include "SCD.h" 46 47#include <SystemConfiguration/VPNAppLayerPrivate.h> 48#include <SystemConfiguration/VPNTunnel.h> 49 50#if !TARGET_OS_IPHONE 51#include <Security/Security.h> 52#include "dy_framework.h" 53#endif // !TARGET_OS_IPHONE 54 55#include <bootstrap.h> 56 57#include <pthread.h> 58#include <notify.h> 59#include <netinet/in.h> 60#include <arpa/inet.h> 61#include <netdb.h> 62#include <unistd.h> 63#include <sys/ioctl.h> 64#include <sys/socket.h> 65#include <net/if.h> 66#include <mach/mach.h> 67#include <bsm/audit.h> 68#include <bsm/libbsm.h> 69#include <sandbox.h> 70#include <sys/proc_info.h> 71#include <libproc.h> 72 73#include <ppp/ppp_msg.h> 74#include "pppcontroller.h" 75#include <ppp/pppcontroller_types.h> 76 77#ifndef PPPCONTROLLER_SERVER_PRIV 78#define PPPCONTROLLER_SERVER_PRIV PPPCONTROLLER_SERVER 79#endif // !PPPCONTROLLER_SERVER_PRIV 80 81static int debug = 0; 82static pthread_once_t initialized = PTHREAD_ONCE_INIT; 83static pthread_mutex_t scnc_lock = PTHREAD_MUTEX_INITIALIZER; 84static mach_port_t scnc_server = MACH_PORT_NULL; 85static char *scnc_server_name = NULL; 86 87 88typedef struct { 89 90 /* base CFType information */ 91 CFRuntimeBase cfBase; 92 93 /* lock */ 94 pthread_mutex_t lock; 95 96 /* service */ 97 SCNetworkServiceRef service; 98 99 /* client info (if we are proxying for another process */ 100 mach_port_t client_audit_session; 101 audit_token_t client_audit_token; 102 mach_port_t client_bootstrap_port; 103 uid_t client_uid; 104 gid_t client_gid; 105 pid_t client_pid; 106 uuid_t client_uuid; 107 CFStringRef client_bundle_id; 108 109 /* ref to PPP controller for control messages */ 110 mach_port_t session_port; 111 112 /* ref to PPP controller for notification messages */ 113 CFMachPortRef notify_port; 114 115 /* keep track of whether we're acquired the initial status */ 116 Boolean haveStatus; 117 118 /* run loop source, callout, context, rl scheduling info */ 119 Boolean scheduled; 120 CFRunLoopSourceRef rls; 121 SCNetworkConnectionCallBack rlsFunction; 122 SCNetworkConnectionContext rlsContext; 123 CFMutableArrayRef rlList; 124 125 /* SCNetworkConnectionSetDispatchQueue */ 126 dispatch_group_t dispatchGroup; 127 dispatch_queue_t dispatchQueue; 128 dispatch_source_t dispatchSource; 129 130 SCNetworkConnectionType type; 131 Boolean on_demand; 132 CFDictionaryRef on_demand_info; 133 CFDictionaryRef on_demand_user_options; 134 CFStringRef on_demand_required_probe; 135 136 /* Flow Divert support info */ 137 CFDictionaryRef flow_divert_token_params; 138 139#if !TARGET_OS_SIMULATOR 140 /* NetworkExtension data structures */ 141 ne_session_t ne_session; 142#endif /* !TARGET_OS_SIMULATOR */ 143} SCNetworkConnectionPrivate, *SCNetworkConnectionPrivateRef; 144 145 146__private_extern__ os_log_t 147__log_SCNetworkConnection(void) 148{ 149 static os_log_t log = NULL; 150 151 if (log == NULL) { 152 log = os_log_create("com.apple.SystemConfiguration", "SCNetworkConnection"); 153 } 154 155 return log; 156} 157 158 159static __inline__ CFTypeRef 160isA_SCNetworkConnection(CFTypeRef obj) 161{ 162 return (isA_CFType(obj, SCNetworkConnectionGetTypeID())); 163} 164 165 166#if !TARGET_OS_SIMULATOR 167static Boolean 168__SCNetworkConnectionUseNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate) 169{ 170 Boolean result = FALSE; 171 172 if (ne_session_use_as_system_vpn() && connectionPrivate->service != NULL) { 173 _SCErrorSet(kSCStatusOK); 174 result = _SCNetworkServiceIsVPN(connectionPrivate->service); 175 /* 176 * SCNetworkServiceGetInterface (called by _SCNetworkServiceIsVPN) will set the SC error to kSCStatusInvalidArgument if the service does not have an associated prefs object. 177 * In that case, we try to get the service type/subtype from the dynamic store. 178 */ 179 if (!result && SCError() == kSCStatusInvalidArgument) { 180 CFStringRef interfaceKey; 181 CFDictionaryRef interfaceDict; 182 CFStringRef serviceID; 183 184 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); 185 interfaceKey = SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault, 186 kSCDynamicStoreDomainSetup, 187 serviceID, 188 kSCEntNetInterface); 189 interfaceDict = SCDynamicStoreCopyValue(NULL, interfaceKey); 190 if (isA_CFDictionary(interfaceDict)) { 191 CFStringRef interfaceType = CFDictionaryGetValue(interfaceDict, kSCPropNetInterfaceType); 192 if (isA_CFString(interfaceType)) { 193 if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP)) { 194 CFStringRef interfaceSubType = CFDictionaryGetValue(interfaceDict, kSCPropNetInterfaceSubType); 195#pragma GCC diagnostic push 196#pragma GCC diagnostic ignored "-Wdeprecated" 197 result = (isA_CFString(interfaceSubType) && 198 (CFEqual(interfaceSubType, kSCValNetInterfaceSubTypePPTP) || 199 CFEqual(interfaceSubType, kSCValNetInterfaceSubTypeL2TP))); 200#pragma GCC diagnostic pop 201 } else { 202 result = (CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) || CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec)); 203 } 204 } 205 } 206 if (interfaceDict != NULL) { 207 CFRelease(interfaceDict); 208 } 209 CFRelease(interfaceKey); 210 } 211 } 212 213 return result; 214} 215#endif /* !TARGET_OS_SIMULATOR */ 216 217 218static Boolean 219__SCNetworkConnectionUsingNetworkExtension(SCNetworkConnectionPrivateRef connectionPrivate) 220{ 221#if !TARGET_OS_SIMULATOR 222 return (connectionPrivate->ne_session != NULL); 223#else 224#pragma unused(connectionPrivate) 225 return FALSE; 226#endif /* !TARGET_OS_SIMULATOR */ 227} 228 229 230static CFStringRef 231__SCNetworkConnectionCopyDescription(CFTypeRef cf) 232{ 233 CFAllocatorRef allocator = CFGetAllocator(cf); 234 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)cf; 235 CFMutableStringRef result; 236 237 result = CFStringCreateMutable(allocator, 0); 238 CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf, allocator); 239 CFStringAppendFormat(result, NULL, CFSTR("service = %p"), connectionPrivate->service); 240 if (connectionPrivate->session_port != MACH_PORT_NULL) { 241 CFStringAppendFormat(result, NULL, CFSTR(", server port = 0x%x"), connectionPrivate->session_port); 242 } 243 CFStringAppendFormat(result, NULL, CFSTR("using NetworkExtension = %s"), (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate) ? "yes" : "no")); 244 CFStringAppendFormat(result, NULL, CFSTR("}")); 245 246 return result; 247} 248 249 250static void 251__SCNetworkConnectionDeallocate(CFTypeRef cf) 252{ 253 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)cf; 254 255 /* release resources */ 256 pthread_mutex_destroy(&connectionPrivate->lock); 257 258 if (connectionPrivate->client_audit_session != MACH_PORT_NULL) { 259 mach_port_deallocate(mach_task_self(), 260 connectionPrivate->client_audit_session); 261 } 262 263 if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) { 264 mach_port_deallocate(mach_task_self(), 265 connectionPrivate->client_bootstrap_port); 266 } 267 268 if (connectionPrivate->client_bundle_id != NULL) { 269 CFRelease(connectionPrivate->client_bundle_id); 270 } 271 272 if (connectionPrivate->rls != NULL) { 273 CFRunLoopSourceInvalidate(connectionPrivate->rls); 274 CFRelease(connectionPrivate->rls); 275 } 276 277 if (connectionPrivate->rlList != NULL) { 278 CFRelease(connectionPrivate->rlList); 279 } 280 281 if (connectionPrivate->notify_port != NULL) { 282 mach_port_t mp = CFMachPortGetPort(connectionPrivate->notify_port); 283 284 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate notify_port", mp); 285 CFMachPortInvalidate(connectionPrivate->notify_port); 286 CFRelease(connectionPrivate->notify_port); 287 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1); 288 } 289 290 if (connectionPrivate->session_port != MACH_PORT_NULL) { 291 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate->session_port); 292 (void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port); 293 } 294 295 if (connectionPrivate->rlsContext.release != NULL) 296 (*connectionPrivate->rlsContext.release)(connectionPrivate->rlsContext.info); 297 298 if (connectionPrivate->service != NULL) { 299 CFRelease(connectionPrivate->service); 300 } 301 302 if (connectionPrivate->on_demand_info != NULL) { 303 CFRelease(connectionPrivate->on_demand_info); 304 } 305 306 if (connectionPrivate->on_demand_user_options != NULL) { 307 CFRelease(connectionPrivate->on_demand_user_options); 308 } 309 310 if (connectionPrivate->on_demand_required_probe != NULL) { 311 CFRelease(connectionPrivate->on_demand_required_probe); 312 } 313 314 if (connectionPrivate->flow_divert_token_params != NULL) { 315 CFRelease(connectionPrivate->flow_divert_token_params); 316 } 317 318#if !TARGET_OS_SIMULATOR 319 if (connectionPrivate->ne_session != NULL) { 320 ne_session_set_event_handler(connectionPrivate->ne_session, NULL, NULL); 321 ne_session_release(connectionPrivate->ne_session); 322 } 323#endif /* !TARGET_OS_SIMULATOR */ 324 325 return; 326} 327 328 329static CFTypeID __kSCNetworkConnectionTypeID = _kCFRuntimeNotATypeID; 330 331static const CFRuntimeClass __SCNetworkConnectionClass = { 332 0, // version 333 "SCNetworkConnection", // className 334 NULL, // init 335 NULL, // copy 336 __SCNetworkConnectionDeallocate, // dealloc 337 NULL, // equal 338 NULL, // hash 339 NULL, // copyFormattingDesc 340 __SCNetworkConnectionCopyDescription // copyDebugDesc 341}; 342 343 344static void 345childForkHandler() 346{ 347 /* the process has forked (and we are the child process) */ 348 349 scnc_server = MACH_PORT_NULL; 350 scnc_server_name = NULL; 351 return; 352} 353 354 355static void 356__SCNetworkConnectionInitialize(void) 357{ 358 char *env; 359 360 /* get the debug environment variable */ 361 env = getenv("PPPDebug"); 362 if (env != NULL) { 363 if (sscanf(env, "%d", &debug) != 1) { 364 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */ 365 debug = 1; 366 } 367 } 368 369 /* register with CoreFoundation */ 370 __kSCNetworkConnectionTypeID = _CFRuntimeRegisterClass(&__SCNetworkConnectionClass); 371 372 /* add handler to cleanup after fork() */ 373 (void) pthread_atfork(NULL, NULL, childForkHandler); 374 375 return; 376} 377 378 379static Boolean 380__SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection); 381 382#define SC_NETWORK_CONNECTION_QUEUE "SCNetworkConnectionQueue" 383 384static dispatch_queue_t 385__SCNetworkConnectionQueue() 386{ 387 static dispatch_once_t once; 388 static dispatch_queue_t q; 389 390 dispatch_once(&once, ^{ 391 q = dispatch_queue_create(SC_NETWORK_CONNECTION_QUEUE, NULL); 392 }); 393 394 return q; 395} 396 397 398static void 399__SCNetworkConnectionNotify(SCNetworkConnectionRef connection, 400 SCNetworkConnectionCallBack rlsFunction, 401 SCNetworkConnectionStatus nc_status, 402 void (*context_release)(const void *), 403 void *context_info) 404{ 405 SC_log(LOG_DEBUG, "exec SCNetworkConnection callout"); 406 (*rlsFunction)(connection, nc_status, context_info); 407 if ((context_release != NULL) && (context_info != NULL)) { 408 (*context_release)(context_info); 409 } 410 411 return; 412} 413 414 415static void 416__SCNetworkConnectionCallBackRunLoopPerform(SCNetworkConnectionRef connection, 417 CFRunLoopRef rl, 418 CFStringRef rl_mode, 419 SCNetworkConnectionCallBack rlsFunction, 420 void (*context_release)(const void *), 421 void *context_info) 422{ 423 SCNetworkConnectionStatus nc_status; 424 425 nc_status = SCNetworkConnectionGetStatus(connection); 426 CFRunLoopPerformBlock(rl, rl_mode, ^{ 427 __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info); 428 CFRelease(connection); 429 }); 430 CFRunLoopWakeUp(rl); 431 return; 432} 433 434static void 435__SCNetworkConnectionCallBackDispatchPerform(SCNetworkConnectionRef connection, 436 dispatch_queue_t q, 437 SCNetworkConnectionCallBack rlsFunction, 438 void (*context_release)(const void *), 439 void *context_info) 440{ 441 SCNetworkConnectionStatus nc_status; 442 443 nc_status = SCNetworkConnectionGetStatus(connection); 444 dispatch_async(q, ^{ 445 __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info); 446 CFRelease(connection); 447 }); 448 return; 449} 450 451 452static void 453__SCNetworkConnectionCallBack(void *connection) 454{ 455 boolean_t exec_async = FALSE; 456 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 457 void *context_info; 458 void (*context_release)(const void *); 459 CFRunLoopRef rl = NULL; 460 CFStringRef rl_mode; 461 SCNetworkConnectionCallBack rlsFunction = NULL; 462 dispatch_queue_t q = NULL; 463 SCNetworkConnectionStatus nc_status = kSCNetworkConnectionInvalid; 464 465 pthread_mutex_lock(&connectionPrivate->lock); 466 467 if (!connectionPrivate->scheduled) { 468 // if not currently scheduled 469 pthread_mutex_unlock(&connectionPrivate->lock); 470 return; 471 } 472 473 rlsFunction = connectionPrivate->rlsFunction; 474 if (rlsFunction == NULL) { 475 pthread_mutex_unlock(&connectionPrivate->lock); 476 return; 477 } 478 479 if ((connectionPrivate->rlsContext.retain != NULL) && (connectionPrivate->rlsContext.info != NULL)) { 480 context_info = (void *)(*connectionPrivate->rlsContext.retain)(connectionPrivate->rlsContext.info); 481 context_release = connectionPrivate->rlsContext.release; 482 } else { 483 context_info = connectionPrivate->rlsContext.info; 484 context_release = NULL; 485 } 486 487#if !TARGET_OS_SIMULATOR 488 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 489 pthread_mutex_unlock(&connectionPrivate->lock); 490 491 nc_status = SCNetworkConnectionGetStatus(connection); 492 __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info); 493 CFRelease(connection); /* This releases the reference that we took in the NESessionEventStatusChanged event handler */ 494 return; 495 } 496#endif /* !TARGET_OS_SIMULATOR */ 497 498 // Do we need to spin a new thread? (either we are running on the main 499 // dispatch queue or main runloop) 500 if (connectionPrivate->rlList == NULL) { 501 // if we are performing the callback on a dispatch queue 502 q = connectionPrivate->dispatchQueue; 503 if (q == dispatch_get_main_queue()) { 504 exec_async = TRUE; 505 } 506 } else { 507 rl = CFRunLoopGetCurrent(); 508 if (rl == CFRunLoopGetMain()) { 509 exec_async = TRUE; 510 } 511 } 512 513 CFRetain(connection); 514 pthread_mutex_unlock(&connectionPrivate->lock); 515 516 if (!exec_async) { 517 nc_status = SCNetworkConnectionGetStatus(connection); 518 __SCNetworkConnectionNotify(connection, rlsFunction, nc_status, context_release, context_info); 519 CFRelease(connection); 520 return; 521 } 522 523 if (connectionPrivate->rlList == NULL) { 524 assert(q != NULL); 525 dispatch_retain(q); 526 dispatch_async(__SCNetworkConnectionQueue(), ^{ 527 __SCNetworkConnectionCallBackDispatchPerform(connection, 528 q, 529 rlsFunction, 530 context_release, 531 context_info); 532 dispatch_release(q); 533 }); 534 } else { 535 assert(rl != NULL); 536 CFRetain(rl); 537 rl_mode = CFRunLoopCopyCurrentMode(rl); 538 dispatch_async(__SCNetworkConnectionQueue(), ^{ 539 __SCNetworkConnectionCallBackRunLoopPerform(connection, 540 rl, 541 rl_mode, 542 rlsFunction, 543 context_release, 544 context_info); 545 CFRelease(rl); 546 CFRelease(rl_mode); 547 }); 548 } 549 550 return; 551} 552 553 554static void 555__SCNetworkConnectionMachCallBack(CFMachPortRef port, void * msg, CFIndex size, void * info) 556{ 557#pragma unused(port) 558#pragma unused(size) 559 mach_no_senders_notification_t *buf = msg; 560 mach_msg_id_t msgid = buf->not_header.msgh_id; 561 SCNetworkConnectionRef connection = (SCNetworkConnectionRef)info; 562 563 if (msgid == MACH_NOTIFY_NO_SENDERS) { 564 // re-establish notification 565 SC_log(LOG_INFO, "PPPController server died"); 566 (void)__SCNetworkConnectionReconnectNotifications(connection); 567 } 568 569 __SCNetworkConnectionCallBack(info); 570 571 return; 572} 573 574 575#pragma mark - 576#pragma mark SCNetworkConnection APIs 577 578 579static CFStringRef 580pppMPCopyDescription(const void *info) 581{ 582 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)info; 583 584 return CFStringCreateWithFormat(NULL, 585 NULL, 586 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"), 587 connectionPrivate, 588 connectionPrivate->service, 589 connectionPrivate->rlsFunction); 590} 591 592 593static SCNetworkConnectionPrivateRef 594__SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator, 595 SCNetworkServiceRef service, 596 SCNetworkConnectionCallBack callout, 597 SCNetworkConnectionContext *context) 598{ 599 SCNetworkConnectionPrivateRef connectionPrivate = NULL; 600 uint32_t size; 601 602 /* initialize runtime */ 603 pthread_once(&initialized, __SCNetworkConnectionInitialize); 604 605 /* allocate NetworkConnection */ 606 size = sizeof(SCNetworkConnectionPrivate) - sizeof(CFRuntimeBase); 607 connectionPrivate = (SCNetworkConnectionPrivateRef)_CFRuntimeCreateInstance(allocator, __kSCNetworkConnectionTypeID, size, NULL); 608 if (connectionPrivate == NULL) { 609 goto fail; 610 } 611 612 /* initialize non-zero/NULL members */ 613 pthread_mutex_init(&connectionPrivate->lock, NULL); 614 if (service != NULL) { 615 connectionPrivate->service = CFRetain(service); 616 } 617 connectionPrivate->client_uid = geteuid(); 618 connectionPrivate->client_gid = getegid(); 619 connectionPrivate->client_pid = getpid(); 620 connectionPrivate->rlsFunction = callout; 621 if (context) { 622 memcpy(&connectionPrivate->rlsContext, context, sizeof(SCNetworkConnectionContext)); 623 if (context->retain != NULL) { 624 connectionPrivate->rlsContext.info = (void *)(*context->retain)(context->info); 625 } 626 } 627 connectionPrivate->type = kSCNetworkConnectionTypeUnknown; 628 629#if !TARGET_OS_SIMULATOR 630 if (__SCNetworkConnectionUseNetworkExtension(connectionPrivate)) { 631 CFStringRef serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); 632 if (serviceID != NULL) { 633 uuid_string_t service_uuid_str; 634 if (CFStringGetCString(serviceID, service_uuid_str, sizeof(service_uuid_str), kCFStringEncodingUTF8)) { 635 uuid_t config_id; 636 if (uuid_parse(service_uuid_str, config_id) == 0) { 637 connectionPrivate->ne_session = ne_session_create(config_id, NESessionTypeVPN); 638 } 639 } 640 } 641 642 if (connectionPrivate->ne_session == NULL) { 643 SC_log(LOG_NOTICE, 644 "SCNetworkConnection failed to create an ne_session: service ID %@ is not a valid UUID", 645 serviceID); 646 goto fail; 647 } 648 } 649#endif /* !TARGET_OS_SIMULATOR */ 650 651 /* success, return the connection reference */ 652 return connectionPrivate; 653 654 fail: 655 656 /* failure, clean up and leave */ 657 if (connectionPrivate != NULL) { 658 CFRelease(connectionPrivate); 659 } 660 661 _SCErrorSet(kSCStatusFailed); 662 return NULL; 663} 664 665 666static mach_port_t 667__SCNetworkConnectionServerPort(kern_return_t *status) 668{ 669 mach_port_t server = MACH_PORT_NULL; 670 671#ifdef BOOTSTRAP_PRIVILEGED_SERVER 672 *status = bootstrap_look_up2(bootstrap_port, 673 __SCNetworkConnectionGetControllerPortName(), 674 &server, 675 0, 676 BOOTSTRAP_PRIVILEGED_SERVER); 677#else // BOOTSTRAP_PRIVILEGED_SERVER 678 *status = bootstrap_look_up(bootstrap_port, __SCNetworkConnectionGetControllerPortName(), &server); 679#endif // BOOTSTRAP_PRIVILEGED_SERVER 680 681 switch (*status) { 682 case BOOTSTRAP_SUCCESS : 683 // service currently registered, "a good thing" (tm) 684 return server; 685 case BOOTSTRAP_NOT_PRIVILEGED : 686 // the service is not privileged 687 break; 688 case BOOTSTRAP_UNKNOWN_SERVICE : 689 // service not currently registered, try again later 690 break; 691 default : 692#ifdef DEBUG 693 SC_log(LOG_DEBUG, "bootstrap_look_up() failed: status=%s", 694 bootstrap_strerror(*status)); 695#endif // DEBUG 696 break; 697 } 698 699 scnc_server_name = NULL; /* reset pppcontroller server */ 700 return MACH_PORT_NULL; 701} 702 703static mach_port_t 704__SCNetworkConnectionGetCurrentServerPort(void) 705{ 706 return scnc_server; 707} 708 709static mach_port_t 710__SCNetworkConnectionRefreshServerPort(mach_port_t current_server, int *mach_result) 711{ 712 mach_port_t new_server; 713 714 pthread_mutex_lock(&scnc_lock); 715 if (scnc_server != MACH_PORT_NULL) { 716 if (current_server == scnc_server) { 717 scnc_server_name = NULL; 718 // if the server we tried returned the error 719 (void)mach_port_deallocate(mach_task_self(), scnc_server); 720 scnc_server = __SCNetworkConnectionServerPort(mach_result); 721 } else { 722 // another thread has refreshed the server port 723 } 724 } else { 725 scnc_server = __SCNetworkConnectionServerPort(mach_result); 726 } 727 new_server = scnc_server; 728 pthread_mutex_unlock(&scnc_lock); 729 730 return new_server; 731} 732 733#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && (!TARGET_OS_SIMULATOR || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) 734#define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION 735#endif 736 737 738#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_OS_SIMULATOR 739#define HAVE_PPPCONTROLLER_ATTACHWITHPROXY 740#endif 741 742static mach_port_t 743__SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate) 744{ 745 void *data = NULL; 746 CFIndex dataLen = 0; 747 CFDataRef dataRef = NULL; 748 mach_port_t notify_port = MACH_PORT_NULL; 749 mach_port_t oldNotify = MACH_PORT_NULL; 750 int retry = 0; 751 int sc_status = kSCStatusFailed; 752 mach_port_t server = __SCNetworkConnectionGetCurrentServerPort(); 753 kern_return_t status = KERN_SUCCESS; 754 755#ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION 756 mach_port_t au_session = MACH_PORT_NULL; 757#endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION 758 759 if (connectionPrivate->session_port != MACH_PORT_NULL) { 760 return connectionPrivate->session_port; 761 } 762 763 if (connectionPrivate->service == NULL) { 764 sc_status = kSCStatusConnectionNoService; 765 goto done; 766 } 767 768 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate->service), &dataRef, &data, &dataLen)) { 769 goto done; 770 } 771 772 if (connectionPrivate->notify_port != NULL) { 773 mach_port_t mp = CFMachPortGetPort(connectionPrivate->notify_port); 774 775 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort mp", mp); 776 CFMachPortInvalidate(connectionPrivate->notify_port); 777 CFRelease(connectionPrivate->notify_port); 778 connectionPrivate->notify_port = NULL; 779 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1); 780 } 781 782#ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION 783 au_session = audit_session_self(); 784#endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION 785 786 // open a new session with the server 787 while (TRUE) { 788 if ((connectionPrivate->rlsFunction != NULL) && (notify_port == MACH_PORT_NULL)) { 789 // allocate port (for server response) 790 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &notify_port); 791 if (status != KERN_SUCCESS) { 792 SC_log(LOG_ERR, "mach_port_allocate() failed: %s", mach_error_string(status)); 793 sc_status = status; 794 goto done; 795 } 796 797 // add send right (passed to the server) 798 status = mach_port_insert_right(mach_task_self(), 799 notify_port, 800 notify_port, 801 MACH_MSG_TYPE_MAKE_SEND); 802 if (status != KERN_SUCCESS) { 803 SC_log(LOG_NOTICE, "mach_port_insert_right() failed: %s", mach_error_string(status)); 804 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1); 805 sc_status = status; 806 goto done; 807 } 808 } 809 810 if (server != MACH_PORT_NULL) { 811#ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY 812 if ((connectionPrivate->client_audit_session == MACH_PORT_NULL) && 813 (connectionPrivate->client_bootstrap_port == MACH_PORT_NULL) && 814 (connectionPrivate->client_uid == geteuid()) && 815 (connectionPrivate->client_gid == getegid()) && 816 (connectionPrivate->client_pid == getpid()) 817 ) { 818#endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY 819 status = pppcontroller_attach(server, 820 data, 821 (mach_msg_type_number_t)dataLen, 822 bootstrap_port, 823 notify_port, 824#ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION 825 au_session, 826#endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION 827 &connectionPrivate->session_port, 828 &sc_status); 829#ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY 830 } else { 831 mach_port_t client_au_session; 832 mach_port_t client_bootstrap_port; 833 834 if (connectionPrivate->client_audit_session == MACH_PORT_NULL) { 835 client_au_session = au_session; 836 } else { 837 client_au_session = connectionPrivate->client_audit_session; 838 } 839 840 if (connectionPrivate->client_bootstrap_port == MACH_PORT_NULL) { 841 client_bootstrap_port = bootstrap_port; 842 } else { 843 client_bootstrap_port = connectionPrivate->client_bootstrap_port; 844 } 845 846 status = pppcontroller_attach_proxy(server, 847 data, 848 (mach_msg_type_number_t)dataLen, 849 client_bootstrap_port, 850 notify_port, 851 client_au_session, 852 connectionPrivate->client_uid, 853 connectionPrivate->client_gid, 854 connectionPrivate->client_pid, 855 &connectionPrivate->session_port, 856 &sc_status); 857 } 858#endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY 859 if (status == KERN_SUCCESS) { 860 if (sc_status != kSCStatusOK) { 861 SC_log(LOG_DEBUG, "attach w/error, sc_status=%s%s", 862 SCErrorString(sc_status), 863 (connectionPrivate->session_port != MACH_PORT_NULL) ? ", w/session_port!=MACH_PORT_NULL" : ""); 864 865 if (connectionPrivate->session_port != MACH_PORT_NULL) { 866 __MACH_PORT_DEBUG(TRUE, 867 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)", 868 connectionPrivate->session_port); 869 mach_port_deallocate(mach_task_self(), connectionPrivate->session_port); 870 connectionPrivate->session_port = MACH_PORT_NULL; 871 } 872 873 if (notify_port != MACH_PORT_NULL) { 874 __MACH_PORT_DEBUG(TRUE, 875 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)", 876 notify_port); 877 (void) mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1); 878 notify_port = MACH_PORT_NULL; 879 } 880 } 881 break; 882 } 883 884 // our [cached] server port is not valid 885 SC_log(LOG_INFO, "!attach: %s", SCErrorString(status)); 886 if (status == MACH_SEND_INVALID_DEST) { 887 // the server is not yet available 888 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port); 889 } else if (status == MIG_SERVER_DIED) { 890 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port); 891 // the server we were using is gone and we've lost our send right 892 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1); 893 notify_port = MACH_PORT_NULL; 894 } else { 895 // if we got an unexpected error, don't retry 896 sc_status = status; 897 break; 898 } 899 } 900 901 server = __SCNetworkConnectionRefreshServerPort(server, &sc_status); 902 if (server == MACH_PORT_NULL) { 903 // if server not available 904 if (sc_status == BOOTSTRAP_UNKNOWN_SERVICE) { 905 // if first retry attempt, wait for SCDynamicStore server 906 if (retry == 0) { 907 SCDynamicStoreRef store; 908 909 store = SCDynamicStoreCreate(NULL, 910 CFSTR("SCNetworkConnection connect"), 911 NULL, 912 NULL); 913 if (store != NULL) { 914 CFRelease(store); 915 } 916 } 917 918 // wait up to 2.5 seconds for the [SCNetworkConnection] server 919 // to startup 920 if ((retry += 50) < 2500) { 921 usleep(50 * 1000); // sleep 50ms between attempts 922 continue; 923 } 924 } 925 break; 926 } 927 } 928 929 if (notify_port != MACH_PORT_NULL) { 930 if (connectionPrivate->session_port != MACH_PORT_NULL) { 931 CFMachPortContext context = { 0 932 , (void *)connectionPrivate 933 , NULL 934 , NULL 935 , pppMPCopyDescription 936 }; 937 938 // request a notification when/if the server dies 939 status = mach_port_request_notification(mach_task_self(), 940 notify_port, 941 MACH_NOTIFY_NO_SENDERS, 942 1, 943 notify_port, 944 MACH_MSG_TYPE_MAKE_SEND_ONCE, 945 &oldNotify); 946 if (status != KERN_SUCCESS) { 947 SC_log(LOG_NOTICE, "mach_port_request_notification() failed: %s", mach_error_string(status)); 948 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1); 949 sc_status = status; 950 goto done; 951 } 952 953 if (oldNotify != MACH_PORT_NULL) { 954 SC_log(LOG_NOTICE, "oldNotify != MACH_PORT_NULL"); 955 } 956 957 // create CFMachPort for SCNetworkConnection notification callback 958 connectionPrivate->notify_port = _SC_CFMachPortCreateWithPort("SCNetworkConnection", 959 notify_port, 960 __SCNetworkConnectionMachCallBack, 961 &context); 962 963 // we need to try a bit harder to acquire the initial status 964 connectionPrivate->haveStatus = FALSE; 965 } else { 966 // with no server port, release the notification port we allocated 967 __MACH_PORT_DEBUG(TRUE, 968 "*** __SCNetworkConnectionSessionPort notify_port (!server)", 969 notify_port); 970 (void) mach_port_mod_refs (mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1); 971 (void) mach_port_deallocate(mach_task_self(), notify_port); 972 notify_port = MACH_PORT_NULL; 973 } 974 } 975 976 done : 977 978 // clean up 979 980#ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION 981 if (au_session != MACH_PORT_NULL) { 982 (void)mach_port_deallocate(mach_task_self(), au_session); 983 } 984#endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION 985 986 if (dataRef != NULL) CFRelease(dataRef); 987 988 switch (sc_status) { 989 case kSCStatusOK : 990 __MACH_PORT_DEBUG(connectionPrivate->session_port != MACH_PORT_NULL, 991 "*** __SCNetworkConnectionSessionPort session_port", 992 connectionPrivate->session_port); 993 __MACH_PORT_DEBUG(notify_port != MACH_PORT_NULL, 994 "*** __SCNetworkConnectionSessionPort notify_port", 995 notify_port); 996 break; 997 case BOOTSTRAP_UNKNOWN_SERVICE : 998 SC_log((status == KERN_SUCCESS) ? LOG_NOTICE : LOG_ERR, "PPPController not available"); 999 break; 1000 default : 1001 SC_log((status == KERN_SUCCESS) ? LOG_NOTICE : LOG_ERR, "pppcontroller_attach() failed: %s", 1002 SCErrorString(sc_status)); 1003 break; 1004 } 1005 1006 if (sc_status != kSCStatusOK) { 1007 _SCErrorSet(sc_status); 1008 } 1009 1010 return connectionPrivate->session_port; 1011} 1012 1013 1014static Boolean 1015__SCNetworkConnectionReconnect(SCNetworkConnectionRef connection) 1016{ 1017 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1018 mach_port_t port; 1019 1020 port = __SCNetworkConnectionSessionPort(connectionPrivate); 1021 return (port != MACH_PORT_NULL); 1022} 1023 1024 1025static Boolean 1026__SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection) 1027{ 1028 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1029 dispatch_group_t dispatchGroup = NULL; 1030 dispatch_queue_t dispatchQueue = NULL; 1031 Boolean ok = TRUE; 1032 CFArrayRef rlList = NULL; 1033 1034 // Before we fully tearing down our [old] notifications, make sure 1035 // we have retained any information that is needed to re-register the 1036 // [new] notifications. 1037 1038 pthread_mutex_lock(&connectionPrivate->lock); 1039 1040 // save and cancel [old] notifications 1041 if (connectionPrivate->rlList != NULL) { 1042 rlList = connectionPrivate->rlList; 1043 connectionPrivate->rlList = NULL; 1044 } 1045 if (connectionPrivate->rls != NULL) { 1046 CFRunLoopSourceInvalidate(connectionPrivate->rls); 1047 CFRelease(connectionPrivate->rls); 1048 connectionPrivate->rls = NULL; 1049 } 1050 if (connectionPrivate->dispatchSource != NULL) { 1051 dispatch_source_cancel(connectionPrivate->dispatchSource); 1052 connectionPrivate->dispatchSource = NULL; 1053 } 1054 1055 // make sure dispatchSource is cancelled before removing group/queue 1056 if (connectionPrivate->dispatchQueue != NULL) { 1057 // save dispatchQueue, release reference when we've queue'd blocks 1058 // complete, allow re-scheduling 1059 dispatchGroup = connectionPrivate->dispatchGroup; 1060 connectionPrivate->dispatchGroup = NULL; 1061 dispatchQueue = connectionPrivate->dispatchQueue; 1062 connectionPrivate->dispatchQueue = NULL; 1063 1064 // and take an extra reference for rescheduling 1065 dispatch_retain(dispatchQueue); 1066 } 1067 1068 connectionPrivate->scheduled = FALSE; 1069 1070 pthread_mutex_unlock(&connectionPrivate->lock); 1071 1072 if (dispatchGroup != NULL) { 1073 dispatch_group_notify(dispatchGroup, dispatchQueue, ^{ 1074 // release group/queue references 1075 dispatch_release(dispatchQueue); 1076 dispatch_release(dispatchGroup); // releases our connection reference 1077 }); 1078 } 1079 1080 // re-schedule 1081 if (rlList != NULL) { 1082 CFIndex i; 1083 CFIndex n; 1084 1085 n = CFArrayGetCount(rlList); 1086 for (i = 0; i < n; i += 3) { 1087 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1); 1088 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(rlList, i+2); 1089 1090 ok = SCNetworkConnectionScheduleWithRunLoop(connection, rl, rlMode); 1091 if (!ok) { 1092 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE) { 1093 SC_log(LOG_NOTICE, "SCNetworkConnectionScheduleWithRunLoop() failed"); 1094 } 1095 goto done; 1096 } 1097 } 1098 } else if (dispatchQueue != NULL) { 1099 ok = SCNetworkConnectionSetDispatchQueue(connection, dispatchQueue); 1100 if (!ok) { 1101 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE) { 1102 SC_log(LOG_NOTICE, "SCNetworkConnectionSetDispatchQueue() failed"); 1103 } 1104 goto done; 1105 } 1106 } else { 1107 ok = FALSE; 1108 } 1109 1110 done : 1111 1112 // cleanup 1113 if (rlList != NULL) { 1114 CFRelease(rlList); 1115 } 1116 if (dispatchQueue != NULL) { 1117 dispatch_release(dispatchQueue); 1118 } 1119 1120 if (!ok) { 1121 SC_log(LOG_NOTICE, "SCNetworkConnection server %s, notification not restored", 1122 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE) ? "shutdown" : "failed"); 1123 } 1124 1125 return ok; 1126} 1127 1128 1129static Boolean 1130__SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection, 1131 const char *error_label, 1132 kern_return_t status, 1133 int *sc_status) 1134{ 1135 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1136 1137 if (status == KERN_SUCCESS) { 1138 return FALSE; 1139 } 1140 1141 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) { 1142 // the server's gone and our session port's dead, remove the dead name right 1143 (void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port); 1144 } else { 1145 // we got an unexpected error, leave the [session] port alone 1146 SC_log(LOG_NOTICE, "%s: %s", error_label, mach_error_string(status)); 1147 } 1148 connectionPrivate->session_port = MACH_PORT_NULL; 1149 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) { 1150 if (__SCNetworkConnectionReconnect(connection)) { 1151 return TRUE; 1152 } 1153 } 1154 *sc_status = status; 1155 1156 return FALSE; 1157} 1158 1159 1160CFTypeID 1161SCNetworkConnectionGetTypeID(void) { 1162 pthread_once(&initialized, __SCNetworkConnectionInitialize); /* initialize runtime */ 1163 return __kSCNetworkConnectionTypeID; 1164} 1165 1166 1167CFArrayRef /* of SCNetworkServiceRef's */ 1168SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set) 1169{ 1170 CFMutableArrayRef available; 1171 Boolean tempSet = FALSE; 1172 1173 if (set == NULL) { 1174 SCPreferencesRef prefs; 1175 1176 prefs = SCPreferencesCreate(NULL, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL); 1177 if (prefs != NULL) { 1178 set = SCNetworkSetCopyCurrent(prefs); 1179 CFRelease(prefs); 1180 } 1181 tempSet = TRUE; 1182 } 1183 1184 available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1185 1186 if (set != NULL) { 1187 CFArrayRef services; 1188 1189 services = SCNetworkSetCopyServices(set); 1190 if (services != NULL) { 1191 CFIndex i; 1192 CFIndex n; 1193 1194 n = CFArrayGetCount(services); 1195 for (i = 0; i < n; i++) { 1196 SCNetworkInterfaceRef interface; 1197 CFStringRef interfaceType; 1198 SCNetworkServiceRef service; 1199 1200 service = CFArrayGetValueAtIndex(services, i); 1201 interface = SCNetworkServiceGetInterface(service); 1202 if (interface == NULL) { 1203 continue; 1204 } 1205 1206 interfaceType = SCNetworkInterfaceGetInterfaceType(interface); 1207 if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP) || 1208 CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) || 1209 CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec)) { 1210 CFArrayAppendValue(available, service); 1211 } 1212 } 1213 1214 CFRelease(services); 1215 } 1216 } 1217 1218 if (tempSet && (set != NULL)) { 1219 CFRelease(set); 1220 } 1221 return available; 1222} 1223 1224 1225SCNetworkConnectionRef 1226SCNetworkConnectionCreateWithService(CFAllocatorRef allocator, 1227 SCNetworkServiceRef service, 1228 SCNetworkConnectionCallBack callout, 1229 SCNetworkConnectionContext *context) 1230{ 1231 SCNetworkConnectionPrivateRef connectionPrivate; 1232 1233 if (!isA_SCNetworkService(service)) { 1234 _SCErrorSet(kSCStatusInvalidArgument); 1235 return FALSE; 1236 } 1237 1238 if (__SCNetworkServiceIsPPTP(service)) { 1239 SC_log(LOG_INFO, "PPTP VPNs are no longer supported"); 1240 _SCErrorSet(kSCStatusConnectionIgnore); 1241 return FALSE; 1242 } 1243 1244 connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, service, callout, context); 1245 return (SCNetworkConnectionRef)connectionPrivate; 1246} 1247 1248 1249SCNetworkConnectionRef 1250SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator, 1251 CFStringRef serviceID, 1252 SCNetworkConnectionCallBack callout, 1253 SCNetworkConnectionContext *context) 1254{ 1255 SCNetworkConnectionRef connection; 1256 SCNetworkServiceRef service; 1257 1258 if (!isA_CFString(serviceID)) { 1259 _SCErrorSet(kSCStatusInvalidArgument); 1260 return NULL; 1261 } 1262 1263 service = _SCNetworkServiceCopyActive(NULL, serviceID); 1264 if (service == NULL) { 1265 return NULL; 1266 } 1267 1268 connection = SCNetworkConnectionCreateWithService(allocator, service, callout, context); 1269 CFRelease(service); 1270 1271 return connection; 1272} 1273 1274 1275SCNetworkConnectionRef 1276SCNetworkConnectionCreate(CFAllocatorRef allocator, 1277 SCNetworkConnectionCallBack callout, 1278 SCNetworkConnectionContext *context) 1279{ 1280 SCNetworkConnectionPrivateRef connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, NULL, callout, context); 1281 return (SCNetworkConnectionRef)connectionPrivate; 1282} 1283 1284 1285CFStringRef 1286SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection) 1287{ 1288 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1289 CFStringRef serviceID; 1290 1291 if (!isA_SCNetworkConnection(connection)) { 1292 _SCErrorSet(kSCStatusInvalidArgument); 1293 return NULL; 1294 } 1295 1296 if (connectionPrivate->service == NULL) { 1297 _SCErrorSet(kSCStatusConnectionNoService); 1298 return NULL; 1299 } 1300 1301 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); 1302 return CFRetain(serviceID); 1303} 1304 1305 1306Boolean 1307SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection, 1308 mach_port_t client_audit_session, 1309 uid_t client_uid, 1310 gid_t client_gid, 1311 pid_t client_pid) 1312{ 1313 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1314 1315 if (!isA_SCNetworkConnection(connection)) { 1316 _SCErrorSet(kSCStatusInvalidArgument); 1317 return FALSE; 1318 } 1319 1320 // save client audit session port 1321 if (connectionPrivate->client_audit_session != MACH_PORT_NULL) { 1322 mach_port_deallocate(mach_task_self(), 1323 connectionPrivate->client_audit_session); 1324 connectionPrivate->client_audit_session = MACH_PORT_NULL; 1325 } 1326 connectionPrivate->client_audit_session = client_audit_session; 1327 if (connectionPrivate->client_audit_session != MACH_PORT_NULL) { 1328 mach_port_mod_refs(mach_task_self(), 1329 connectionPrivate->client_audit_session, 1330 MACH_PORT_RIGHT_SEND, 1331 +1); 1332 } 1333 1334 // save client UID, GID, and PID 1335 connectionPrivate->client_uid = client_uid; 1336 connectionPrivate->client_gid = client_gid; 1337 connectionPrivate->client_pid = client_pid; 1338 1339 return TRUE; 1340} 1341 1342 1343Boolean 1344SCNetworkConnectionSetClientAuditInfo(SCNetworkConnectionRef connection, 1345 audit_token_t client_audit_token, 1346 mach_port_t audit_session, 1347 mach_port_t bootstrap_port, 1348 pid_t client_pid, 1349 const uuid_t uuid, 1350 const char *bundle_id) 1351{ 1352 const audit_token_t null_audit = KERNEL_AUDIT_TOKEN_VALUE; 1353 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1354 gid_t gid = 0; 1355 pid_t pid = 0; 1356 uid_t uid = 0; 1357 1358 if (memcmp(&client_audit_token, &null_audit, sizeof(client_audit_token))) { 1359#if TARGET_OS_IPHONE 1360 audit_token_to_au32(client_audit_token, NULL, &uid, &gid, NULL, NULL, &pid, NULL, NULL); 1361#else // TARGET_OS_IPHONE 1362 uid = audit_token_to_euid(client_audit_token); 1363 gid = audit_token_to_egid(client_audit_token); 1364 pid = audit_token_to_pid(client_audit_token); 1365#endif // TARGET_OS_IPHONE 1366 } else { 1367 pid = client_pid; 1368 } 1369 1370 if (!SCNetworkConnectionSetClientInfo(connection, audit_session, uid, gid, pid)) { 1371 return FALSE; 1372 } 1373 1374 if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) { 1375 mach_port_deallocate(mach_task_self(), 1376 connectionPrivate->client_bootstrap_port); 1377 connectionPrivate->client_bootstrap_port = MACH_PORT_NULL; 1378 } 1379 1380 connectionPrivate->client_bootstrap_port = bootstrap_port; 1381 if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) { 1382 mach_port_mod_refs(mach_task_self(), 1383 connectionPrivate->client_bootstrap_port, 1384 MACH_PORT_RIGHT_SEND, 1385 +1); 1386 } 1387 1388 memcpy(&connectionPrivate->client_audit_token, &client_audit_token, sizeof(connectionPrivate->client_audit_token)); 1389 1390 if (uuid != NULL && !uuid_is_null(uuid)) { 1391 uuid_copy(connectionPrivate->client_uuid, uuid); 1392 } 1393 1394 if (connectionPrivate->client_bundle_id != NULL) { 1395 CFRelease(connectionPrivate->client_bundle_id); 1396 connectionPrivate->client_bundle_id = NULL; 1397 } 1398 1399 if (bundle_id != NULL) { 1400 connectionPrivate->client_bundle_id = CFStringCreateWithCString(kCFAllocatorDefault, bundle_id, kCFStringEncodingUTF8); 1401 } 1402 1403 return TRUE; 1404} 1405 1406 1407CFDictionaryRef 1408SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection) 1409{ 1410 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1411 xmlDataOut_t data = NULL; 1412 mach_msg_type_number_t datalen = 0; 1413 int sc_status = kSCStatusFailed; 1414 mach_port_t session_port; 1415 CFPropertyListRef statistics = NULL; 1416 kern_return_t status; 1417 1418 if (!isA_SCNetworkConnection(connection)) { 1419 _SCErrorSet(kSCStatusInvalidArgument); 1420 return NULL; 1421 } 1422 1423 pthread_mutex_lock(&connectionPrivate->lock); 1424 1425#if !TARGET_OS_SIMULATOR 1426 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 1427 __block xpc_object_t xstats = NULL; 1428 ne_session_t ne_session = connectionPrivate->ne_session; 1429 1430 ne_session_retain(ne_session); 1431 pthread_mutex_unlock(&connectionPrivate->lock); 1432 1433 dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0); 1434 ne_session_get_info(ne_session, NESessionInfoTypeStatistics, __SCNetworkConnectionQueue(), ^(xpc_object_t result) { 1435 if (result != NULL) { 1436 xstats = xpc_retain(result); 1437 } 1438 ne_session_release(ne_session); 1439 dispatch_semaphore_signal(ne_sema); 1440 }); 1441 dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER); 1442 dispatch_release(ne_sema); 1443 1444 if (xstats != NULL) { 1445 statistics = _CFXPCCreateCFObjectFromXPCObject(xstats); 1446 xpc_release(xstats); 1447 } else { 1448 _SCErrorSet(kSCStatusFailed); 1449 } 1450 1451 return statistics; 1452 } 1453#endif /* !TARGET_OS_SIMULATOR */ 1454 1455 retry : 1456 1457 session_port = __SCNetworkConnectionSessionPort(connectionPrivate); 1458 if (session_port == MACH_PORT_NULL) { 1459 goto done; 1460 } 1461 1462 status = pppcontroller_copystatistics(session_port, &data, &datalen, &sc_status); 1463 if (__SCNetworkConnectionNeedsRetry(connection, 1464 "SCNetworkConnectionCopyStatistics()", 1465 status, 1466 &sc_status)) { 1467 goto retry; 1468 } 1469 1470 if (data != NULL) { 1471 if (!_SCUnserialize(&statistics, NULL, data, datalen)) { 1472 if (sc_status != kSCStatusOK) sc_status = SCError(); 1473 } 1474 if ((sc_status == kSCStatusOK) && !isA_CFDictionary(statistics)) { 1475 sc_status = kSCStatusFailed; 1476 } 1477 } 1478 1479 if (sc_status != kSCStatusOK) { 1480 if (statistics != NULL) { 1481 CFRelease(statistics); 1482 statistics = NULL; 1483 } 1484 _SCErrorSet(sc_status); 1485 } 1486 1487 done : 1488 1489 pthread_mutex_unlock(&connectionPrivate->lock); 1490 return statistics; 1491} 1492 1493 1494SCNetworkServiceRef 1495SCNetworkConnectionGetService(SCNetworkConnectionRef connection) 1496{ 1497 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1498 1499 if (!isA_SCNetworkConnection(connection)) { 1500 _SCErrorSet(kSCStatusInvalidArgument); 1501 return NULL; 1502 } 1503 1504 return connectionPrivate->service; 1505} 1506 1507 1508SCNetworkConnectionStatus 1509SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection) 1510{ 1511 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1512 SCNetworkConnectionStatus nc_status = kSCNetworkConnectionInvalid; 1513 int retry = 0; 1514 int sc_status = kSCStatusFailed; 1515 mach_port_t session_port; 1516 kern_return_t status; 1517 CFStringRef serviceID; 1518 1519 if (!isA_SCNetworkConnection(connection)) { 1520 _SCErrorSet(kSCStatusInvalidArgument); 1521 return kSCNetworkConnectionInvalid; 1522 } 1523 1524 if (connectionPrivate->service == NULL) { 1525 _SCErrorSet(kSCStatusConnectionNoService); 1526 return kSCNetworkConnectionInvalid; 1527 } 1528 1529 // skip retry and return immediately if we know no service is to be found. 1530 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); 1531 if (CFStringGetLength(serviceID) == 0) { 1532 _SCErrorSet(kSCStatusConnectionNoService); 1533 return kSCNetworkConnectionInvalid; 1534 } 1535 1536 pthread_mutex_lock(&connectionPrivate->lock); 1537 1538#if !TARGET_OS_SIMULATOR 1539 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 1540 __block ne_session_status_t ne_status; 1541 ne_session_t ne_session = connectionPrivate->ne_session; 1542 1543 ne_session_retain(ne_session); 1544 pthread_mutex_unlock(&connectionPrivate->lock); 1545 1546 dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0); 1547 ne_session_get_status(ne_session, __SCNetworkConnectionQueue(), ^(ne_session_status_t status) { 1548 ne_status = status; 1549 ne_session_release(ne_session); 1550 dispatch_semaphore_signal(ne_sema); 1551 }); 1552 dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER); 1553 dispatch_release(ne_sema); 1554 1555 return SCNetworkConnectionGetStatusFromNEStatus(ne_status); 1556 } 1557#endif /* !TARGET_OS_SIMULATOR */ 1558 1559 retry : 1560 1561 session_port = __SCNetworkConnectionSessionPort(connectionPrivate); 1562 if (session_port == MACH_PORT_NULL) { 1563 nc_status = kSCNetworkConnectionInvalid; 1564 goto done; 1565 } 1566 1567 status = pppcontroller_getstatus(session_port, &nc_status, &sc_status); 1568 if (__SCNetworkConnectionNeedsRetry(connection, 1569 "SCNetworkConnectionGetStatus()", 1570 status, 1571 &sc_status)) { 1572 goto retry; 1573 } 1574 1575 // wait up to 250 ms for the network service to become available 1576 if (!connectionPrivate->haveStatus && 1577 (sc_status == kSCStatusConnectionNoService) && 1578 ((retry += 10) < 250)) { 1579 usleep(10 * 1000); // sleep 10ms between attempts 1580 goto retry; 1581 } 1582 1583 if (sc_status == kSCStatusOK) { 1584 connectionPrivate->haveStatus = TRUE; 1585 } else { 1586 _SCErrorSet(sc_status); 1587 nc_status = kSCNetworkConnectionInvalid; 1588 } 1589 1590 done : 1591 1592 pthread_mutex_unlock(&connectionPrivate->lock); 1593 return nc_status; 1594} 1595 1596 1597CFDictionaryRef 1598SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection) 1599{ 1600 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1601 xmlDataOut_t data = NULL; 1602 mach_msg_type_number_t datalen = 0; 1603 CFPropertyListRef extstatus = NULL; 1604 int retry = 0; 1605 int sc_status = kSCStatusFailed; 1606 mach_port_t session_port; 1607 kern_return_t status; 1608 CFStringRef serviceID; 1609 1610 if (!isA_SCNetworkConnection(connection)) { 1611 _SCErrorSet(kSCStatusInvalidArgument); 1612 return NULL; 1613 } 1614 1615 if (connectionPrivate->service == NULL) { 1616 _SCErrorSet(kSCStatusConnectionNoService); 1617 return NULL; 1618 } 1619 1620 // skip retry and return immediately if we know no service is to be found. 1621 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); 1622 if (CFStringGetLength(serviceID) == 0) { 1623 _SCErrorSet(kSCStatusConnectionNoService); 1624 return NULL; 1625 } 1626 1627 pthread_mutex_lock(&connectionPrivate->lock); 1628 1629#if !TARGET_OS_SIMULATOR 1630 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 1631 __block CFDictionaryRef statusDictionary = NULL; 1632 ne_session_t ne_session = connectionPrivate->ne_session; 1633 1634 ne_session_retain(ne_session); 1635 pthread_mutex_unlock(&connectionPrivate->lock); 1636 1637 dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0); 1638 ne_session_get_info(ne_session, NESessionInfoTypeExtendedStatus, __SCNetworkConnectionQueue(), ^(xpc_object_t extended_status) { 1639 if (extended_status != NULL) { 1640 statusDictionary = _CFXPCCreateCFObjectFromXPCObject(extended_status); 1641 ne_session_release(ne_session); 1642 dispatch_semaphore_signal(ne_sema); 1643 } else { 1644 ne_session_get_status(ne_session, __SCNetworkConnectionQueue(), ^(ne_session_status_t ne_status) { 1645 SCNetworkConnectionStatus status = SCNetworkConnectionGetStatusFromNEStatus(ne_status); 1646 if (status != kSCNetworkConnectionInvalid) { 1647 CFStringRef keys[1] = { kSCNetworkConnectionStatus }; 1648 CFNumberRef values[1] = { NULL }; 1649 values[0] = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &status); 1650 statusDictionary = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, sizeof(values) / sizeof(values[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1651 CFRelease(values[0]); 1652 } 1653 ne_session_release(ne_session); 1654 dispatch_semaphore_signal(ne_sema); 1655 }); 1656 } 1657 }); 1658 dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER); 1659 dispatch_release(ne_sema); 1660 1661 if (statusDictionary != NULL) { 1662 extstatus = (CFPropertyListRef)statusDictionary; 1663 } else { 1664 _SCErrorSet(kSCStatusFailed); 1665 } 1666 1667 return extstatus; 1668 } 1669#endif 1670 1671 retry : 1672 1673 session_port = __SCNetworkConnectionSessionPort(connectionPrivate); 1674 if (session_port == MACH_PORT_NULL) { 1675 goto done; 1676 } 1677 1678 status = pppcontroller_copyextendedstatus(session_port, &data, &datalen, &sc_status); 1679 if (__SCNetworkConnectionNeedsRetry(connection, 1680 "SCNetworkConnectionCopyExtendedStatus()", 1681 status, 1682 &sc_status)) { 1683 goto retry; 1684 } 1685 1686 if (data != NULL) { 1687 if (!_SCUnserialize(&extstatus, NULL, data, datalen)) { 1688 if (sc_status != kSCStatusOK) sc_status = SCError(); 1689 } 1690 if ((sc_status == kSCStatusOK) && !isA_CFDictionary(extstatus)) { 1691 sc_status = kSCStatusFailed; 1692 } 1693 } 1694 1695 // wait up to 250 ms for the network service to become available 1696 if (!connectionPrivate->haveStatus && 1697 (sc_status == kSCStatusConnectionNoService) && 1698 ((retry += 10) < 250)) { 1699 usleep(10 * 1000); // sleep 10ms between attempts 1700 goto retry; 1701 } 1702 1703 if (sc_status == kSCStatusOK) { 1704 connectionPrivate->haveStatus = TRUE; 1705 } else { 1706 if (extstatus != NULL) { 1707 CFRelease(extstatus); 1708 extstatus = NULL; 1709 } 1710 _SCErrorSet(sc_status); 1711 } 1712 1713 done : 1714 1715 pthread_mutex_unlock(&connectionPrivate->lock); 1716 return extstatus; 1717} 1718 1719 1720static void 1721_SCNetworkConnectionMergeDictionaries (const void *key, const void *value, void *context) 1722{ 1723 /* Add value only if not present */ 1724 CFDictionaryAddValue((CFMutableDictionaryRef)context, (CFStringRef)key, (CFTypeRef)value); 1725} 1726 1727 1728Boolean 1729SCNetworkConnectionStart(SCNetworkConnectionRef connection, 1730 CFDictionaryRef userOptions, 1731 Boolean linger) 1732{ 1733 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1734 CFDataRef dataref = NULL; 1735 void *data = NULL; 1736 CFIndex datalen = 0; 1737 Boolean ok = FALSE; 1738 int sc_status = kSCStatusFailed; 1739 mach_port_t session_port; 1740 kern_return_t status; 1741 1742 if (!isA_SCNetworkConnection(connection)) { 1743 _SCErrorSet(kSCStatusInvalidArgument); 1744 return FALSE; 1745 } 1746 1747 if ((userOptions != NULL) && !isA_CFDictionary(userOptions)) { 1748 _SCErrorSet(kSCStatusInvalidArgument); 1749 return FALSE; 1750 } 1751 1752 if (userOptions == NULL) { 1753 userOptions = connectionPrivate->on_demand_user_options; 1754 } else if (connectionPrivate->on_demand_user_options != NULL) { 1755 CFDictionaryRef localUserOptions = NULL; 1756 1757 localUserOptions = CFDictionaryCreateMutableCopy(NULL, 0, userOptions); 1758 if (localUserOptions) { 1759 CFDictionaryApplyFunction(connectionPrivate->on_demand_user_options, 1760 _SCNetworkConnectionMergeDictionaries, 1761 (void *)localUserOptions); 1762 CFRelease(connectionPrivate->on_demand_user_options); 1763 userOptions = connectionPrivate->on_demand_user_options = localUserOptions; 1764 } 1765 } 1766 1767 if (debug > 0) { 1768 CFMutableDictionaryRef mdict = NULL; 1769 1770 SC_log(LOG_INFO, "SCNetworkConnectionStart (%p)", connectionPrivate); 1771 1772 if (userOptions != NULL) { 1773 CFDictionaryRef dict; 1774 CFStringRef encryption; 1775 CFMutableDictionaryRef new_dict; 1776 1777 /* special code to remove secret information */ 1778 mdict = CFDictionaryCreateMutableCopy(NULL, 0, userOptions); 1779 1780 dict = CFDictionaryGetValue(mdict, kSCEntNetPPP); 1781 if (isA_CFDictionary(dict)) { 1782 encryption = CFDictionaryGetValue(dict, kSCPropNetPPPAuthPasswordEncryption); 1783 if (!isA_CFString(encryption) || 1784 !CFEqual(encryption, kSCValNetPPPAuthPasswordEncryptionKeychain)) { 1785 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 1786 CFDictionaryReplaceValue(new_dict, kSCPropNetPPPAuthPassword, CFSTR("******")); 1787 CFDictionarySetValue(mdict, kSCEntNetPPP, new_dict); 1788 CFRelease(new_dict); 1789 } 1790 } 1791 1792 dict = CFDictionaryGetValue(mdict, kSCEntNetL2TP); 1793 if (isA_CFDictionary(dict)) { 1794 encryption = CFDictionaryGetValue(dict, kSCPropNetL2TPIPSecSharedSecretEncryption); 1795 if (!isA_CFString(encryption) || 1796 !CFEqual(encryption, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain)) { 1797 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 1798 CFDictionaryReplaceValue(new_dict, kSCPropNetL2TPIPSecSharedSecret, CFSTR("******")); 1799 CFDictionarySetValue(mdict, kSCEntNetL2TP, new_dict); 1800 CFRelease(new_dict); 1801 } 1802 } 1803 1804 dict = CFDictionaryGetValue(mdict, kSCEntNetIPSec); 1805 if (isA_CFDictionary(dict)) { 1806 encryption = CFDictionaryGetValue(dict, kSCPropNetIPSecSharedSecretEncryption); 1807 if (!isA_CFString(encryption) || 1808 !CFEqual(encryption, kSCValNetIPSecSharedSecretEncryptionKeychain)) { 1809 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict); 1810 CFDictionaryReplaceValue(new_dict, kSCPropNetIPSecSharedSecret, CFSTR("******")); 1811 CFDictionarySetValue(mdict, kSCEntNetIPSec, new_dict); 1812 CFRelease(new_dict); 1813 } 1814 } 1815 } 1816 1817 SC_log(LOG_INFO, "User options: %@", mdict); 1818 if (mdict != NULL) CFRelease(mdict); 1819 } 1820 1821 pthread_mutex_lock(&connectionPrivate->lock); 1822 1823 /* Clear out any cached flow divert token parameters */ 1824 if (connectionPrivate->flow_divert_token_params != NULL) { 1825 CFRelease(connectionPrivate->flow_divert_token_params); 1826 connectionPrivate->flow_divert_token_params = NULL; 1827 } 1828 1829#if !TARGET_OS_SIMULATOR 1830 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 1831 xpc_object_t xuser_options = NULL; 1832 1833 if (userOptions != NULL) { 1834 xuser_options = _CFXPCCreateXPCObjectFromCFObject(userOptions); 1835 } 1836 1837 if (connectionPrivate->client_bootstrap_port != MACH_PORT_NULL) { 1838#if NE_SESSION_VERSION > 2 1839 ne_session_start_on_behalf_of(connectionPrivate->ne_session, 1840 xuser_options, 1841 connectionPrivate->client_bootstrap_port, 1842 connectionPrivate->client_audit_session, 1843 connectionPrivate->client_uid, 1844 connectionPrivate->client_gid, 1845 connectionPrivate->client_pid); 1846#else 1847 ne_session_start_on_behalf_of(connectionPrivate->ne_session, 1848 xuser_options, 1849 connectionPrivate->client_bootstrap_port, 1850 connectionPrivate->client_audit_session, 1851 connectionPrivate->client_uid, 1852 connectionPrivate->client_gid); 1853#endif 1854 } else { 1855 ne_session_start_with_options(connectionPrivate->ne_session, xuser_options); 1856 } 1857 1858 /* make sure the xpc_message goes through */ 1859 ne_session_send_barrier(connectionPrivate->ne_session); 1860 1861 if (xuser_options != NULL) { 1862 xpc_release(xuser_options); 1863 } 1864 1865 ok = TRUE; 1866 goto done; 1867 } 1868#endif /* !TARGET_OS_SIMULATOR */ 1869 1870 if (userOptions && !_SCSerialize(userOptions, &dataref, &data, &datalen)) { 1871 goto done; 1872 } 1873 1874 retry : 1875 1876 session_port = __SCNetworkConnectionSessionPort(connectionPrivate); 1877 if (session_port == MACH_PORT_NULL) { 1878 if (dataref) CFRelease(dataref); 1879 goto done; 1880 } 1881 1882 status = pppcontroller_start(session_port, 1883 data, 1884 (mach_msg_type_number_t)datalen, 1885 linger, 1886 &sc_status); 1887 if (__SCNetworkConnectionNeedsRetry(connection, 1888 "SCNetworkConnectionStart()", 1889 status, 1890 &sc_status)) { 1891 goto retry; 1892 } 1893 1894 if (dataref) CFRelease(dataref); 1895 1896 if (debug > 0) { 1897 SC_log(LOG_INFO, "SCNetworkConnectionStart (%p), return: %d", connectionPrivate, sc_status); 1898 } 1899 1900 if (sc_status != kSCStatusOK) { 1901 _SCErrorSet(sc_status); 1902 goto done; 1903 } 1904 1905 /* connection is now started */ 1906 ok = TRUE; 1907 1908 done: 1909 pthread_mutex_unlock(&connectionPrivate->lock); 1910 return ok; 1911} 1912 1913 1914Boolean 1915SCNetworkConnectionStop(SCNetworkConnectionRef connection, 1916 Boolean forceDisconnect) 1917{ 1918 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1919 Boolean ok = FALSE; 1920 int sc_status = kSCStatusFailed; 1921 mach_port_t session_port; 1922 kern_return_t status; 1923 1924 if (!isA_SCNetworkConnection(connection)) { 1925 _SCErrorSet(kSCStatusInvalidArgument); 1926 return FALSE; 1927 } 1928 1929 if (debug > 0) { 1930 SC_log(LOG_INFO, "SCNetworkConnectionStop (%p)", connectionPrivate); 1931 } 1932 1933 pthread_mutex_lock(&connectionPrivate->lock); 1934 1935#if !TARGET_OS_SIMULATOR 1936 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 1937 ne_session_stop(connectionPrivate->ne_session); 1938 /* make sure the xpc_message goes through */ 1939 ne_session_send_barrier(connectionPrivate->ne_session); 1940 ok = TRUE; 1941 goto done; 1942 } 1943#endif /* !TARGET_OS_SIMULATOR */ 1944 1945 retry : 1946 1947 session_port = __SCNetworkConnectionSessionPort(connectionPrivate); 1948 if (session_port == MACH_PORT_NULL) { 1949 goto done; 1950 } 1951 1952 status = pppcontroller_stop(session_port, forceDisconnect, &sc_status); 1953 if (__SCNetworkConnectionNeedsRetry(connection, 1954 "SCNetworkConnectionStop()", 1955 status, 1956 &sc_status)) { 1957 goto retry; 1958 } 1959 1960 if (debug > 0) { 1961 SC_log(LOG_INFO, "SCNetworkConnectionStop (%p), return: %d", connectionPrivate, sc_status); 1962 } 1963 1964 if (sc_status != kSCStatusOK) { 1965 _SCErrorSet(sc_status); 1966 goto done; 1967 } 1968 1969 /* connection is now disconnecting */ 1970 ok = TRUE; 1971 1972 done : 1973 1974 pthread_mutex_unlock(&connectionPrivate->lock); 1975 return ok; 1976} 1977 1978 1979Boolean 1980SCNetworkConnectionSuspend(SCNetworkConnectionRef connection) 1981{ 1982 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 1983 Boolean ok = FALSE; 1984 int sc_status = kSCStatusFailed; 1985 mach_port_t session_port; 1986 kern_return_t status; 1987 1988 if (!isA_SCNetworkConnection(connection)) { 1989 _SCErrorSet(kSCStatusInvalidArgument); 1990 return FALSE; 1991 } 1992 1993 if (debug > 0) { 1994 SC_log(LOG_INFO, "SCNetworkConnectionSuspend (%p)", connectionPrivate); 1995 } 1996 1997 pthread_mutex_lock(&connectionPrivate->lock); 1998 1999#if !!TARGET_OS_SIMULATOR 2000 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 2001 /* Suspend only applies to PPPSerial and PPPoE */ 2002 ok = TRUE; 2003 goto done; 2004 } 2005#endif /* !TARGET_OS_SIMULATOR */ 2006 2007 retry : 2008 2009 session_port = __SCNetworkConnectionSessionPort(connectionPrivate); 2010 if (session_port == MACH_PORT_NULL) { 2011 goto done; 2012 } 2013 2014 status = pppcontroller_suspend(session_port, &sc_status); 2015 if (__SCNetworkConnectionNeedsRetry(connection, 2016 "SCNetworkConnectionSuspend()", 2017 status, 2018 &sc_status)) { 2019 goto retry; 2020 } 2021 2022 if (debug > 0) { 2023 SC_log(LOG_INFO, "SCNetworkConnectionSuspend (%p), return: %d", connectionPrivate, sc_status); 2024 } 2025 2026 if (sc_status != kSCStatusOK) { 2027 _SCErrorSet(sc_status); 2028 goto done; 2029 } 2030 2031 /* connection is now suspended */ 2032 ok = TRUE; 2033 2034 done : 2035 2036 pthread_mutex_unlock(&connectionPrivate->lock); 2037 return ok; 2038} 2039 2040 2041Boolean 2042SCNetworkConnectionResume(SCNetworkConnectionRef connection) 2043{ 2044 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 2045 Boolean ok = FALSE; 2046 int sc_status = kSCStatusFailed; 2047 mach_port_t session_port; 2048 kern_return_t status; 2049 2050 if (!isA_SCNetworkConnection(connection)) { 2051 _SCErrorSet(kSCStatusInvalidArgument); 2052 return FALSE; 2053 } 2054 2055 if (debug > 0) { 2056 SC_log(LOG_INFO, "SCNetworkConnectionResume (%p)", connectionPrivate); 2057 } 2058 2059 pthread_mutex_lock(&connectionPrivate->lock); 2060 2061#if !TARGET_OS_SIMULATOR 2062 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 2063 /* Resume only applies to PPPSerial and PPPoE */ 2064 ok = TRUE; 2065 goto done; 2066 } 2067#endif /* !TARGET_OS_SIMULATOR */ 2068 2069 retry : 2070 2071 session_port = __SCNetworkConnectionSessionPort(connectionPrivate); 2072 if (session_port == MACH_PORT_NULL) { 2073 goto done; 2074 } 2075 2076 status = pppcontroller_resume(session_port, &sc_status); 2077 if (__SCNetworkConnectionNeedsRetry(connection, 2078 "SCNetworkConnectionResume()", 2079 status, 2080 &sc_status)) { 2081 goto retry; 2082 } 2083 2084 if (debug > 0) { 2085 SC_log(LOG_INFO, "SCNetworkConnectionResume (%p), return: %d", connectionPrivate, sc_status); 2086 } 2087 2088 if (sc_status != kSCStatusOK) { 2089 _SCErrorSet(sc_status); 2090 goto done; 2091 } 2092 2093 /* connection is now resume */ 2094 ok = TRUE; 2095 2096 done : 2097 2098 pthread_mutex_unlock(&connectionPrivate->lock); 2099 return ok; 2100} 2101 2102 2103#if !TARGET_OS_SIMULATOR 2104Boolean 2105SCNetworkConnectionRefreshOnDemandState(__unused SCNetworkConnectionRef connection) 2106{ 2107 return FALSE; 2108} 2109#endif /* !TARGET_OS_SIMULATOR */ 2110 2111 2112CFDictionaryRef 2113SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection) 2114{ 2115 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 2116 xmlDataOut_t data = NULL; 2117 mach_msg_type_number_t datalen = 0; 2118 int sc_status = kSCStatusFailed; 2119 mach_port_t session_port; 2120 kern_return_t status; 2121 CFPropertyListRef userOptions = NULL; 2122 2123 if (!isA_SCNetworkConnection(connection)) { 2124 _SCErrorSet(kSCStatusInvalidArgument); 2125 return NULL; 2126 } 2127 2128 pthread_mutex_lock(&connectionPrivate->lock); 2129 2130#if !TARGET_OS_SIMULATOR 2131 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 2132 __block xpc_object_t config = NULL; 2133 ne_session_t ne_session = connectionPrivate->ne_session; 2134 2135 ne_session_retain(ne_session); 2136 pthread_mutex_unlock(&connectionPrivate->lock); 2137 2138 dispatch_semaphore_t ne_sema = dispatch_semaphore_create(0); 2139 ne_session_get_info(ne_session, NESessionInfoTypeConfiguration, __SCNetworkConnectionQueue(), ^(xpc_object_t result) { 2140 if (result != NULL) { 2141 config = xpc_retain(result); 2142 } 2143 ne_session_release(ne_session); 2144 dispatch_semaphore_signal(ne_sema); 2145 }); 2146 dispatch_semaphore_wait(ne_sema, DISPATCH_TIME_FOREVER); 2147 dispatch_release(ne_sema); 2148 2149 if (config != NULL) { 2150 xpc_object_t xoptions = xpc_dictionary_get_value(config, NESMSessionLegacyUserConfigurationKey); 2151 if (xoptions != NULL) { 2152 userOptions = _CFXPCCreateCFObjectFromXPCObject(xoptions); 2153 } 2154 xpc_release(config); 2155 } 2156 return userOptions; 2157 } 2158#endif /* !TARGET_OS_SIMULATOR */ 2159 2160 retry : 2161 2162 session_port = __SCNetworkConnectionSessionPort(connectionPrivate); 2163 if (session_port == MACH_PORT_NULL) { 2164 goto done; 2165 } 2166 2167 status = pppcontroller_copyuseroptions(session_port, &data, &datalen, &sc_status); 2168 if (__SCNetworkConnectionNeedsRetry(connection, 2169 "SCNetworkConnectionCopyUserOptions()", 2170 status, 2171 &sc_status)) { 2172 goto retry; 2173 } 2174 2175 if (data != NULL) { 2176 if (!_SCUnserialize(&userOptions, NULL, data, datalen)) { 2177 if (sc_status != kSCStatusOK) sc_status = SCError(); 2178 } 2179 if ((sc_status == kSCStatusOK) && (userOptions != NULL) && !isA_CFDictionary(userOptions)) { 2180 sc_status = kSCStatusFailed; 2181 } 2182 } 2183 2184 if (sc_status == kSCStatusOK) { 2185 if (userOptions == NULL) { 2186 // if no user options, return an empty dictionary 2187 userOptions = CFDictionaryCreate(NULL, 2188 NULL, 2189 NULL, 2190 0, 2191 &kCFTypeDictionaryKeyCallBacks, 2192 &kCFTypeDictionaryValueCallBacks); 2193 } 2194 } else { 2195 if (userOptions) { 2196 CFRelease(userOptions); 2197 userOptions = NULL; 2198 } 2199 _SCErrorSet(sc_status); 2200 } 2201 2202 done : 2203 2204 pthread_mutex_unlock(&connectionPrivate->lock); 2205 return userOptions; 2206} 2207 2208 2209static Boolean 2210__SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection, 2211 CFRunLoopRef runLoop, 2212 CFStringRef runLoopMode, 2213 dispatch_queue_t queue) 2214{ 2215 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 2216 Boolean ok = FALSE; 2217 int sc_status = kSCStatusFailed; 2218 mach_port_t session_port; 2219 kern_return_t status; 2220 2221 pthread_mutex_lock(&connectionPrivate->lock); 2222 2223 if (connectionPrivate->rlsFunction == NULL) { 2224 _SCErrorSet(kSCStatusInvalidArgument); 2225 goto done; 2226 } 2227 2228 if ((connectionPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue 2229 ((queue != NULL) && connectionPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop 2230 _SCErrorSet(kSCStatusInvalidArgument); 2231 goto done; 2232 } 2233 2234 if (!connectionPrivate->scheduled) { 2235 2236 retry : 2237 2238 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 2239 session_port = __SCNetworkConnectionSessionPort(connectionPrivate); 2240 if (session_port == MACH_PORT_NULL) { 2241 goto done; 2242 } 2243 2244 status = pppcontroller_notification(session_port, 1, &sc_status); 2245 if (__SCNetworkConnectionNeedsRetry(connection, 2246 "__SCNetworkConnectionScheduleWithRunLoop()", 2247 status, 2248 &sc_status)) { 2249 goto retry; 2250 } 2251 2252 if (sc_status != kSCStatusOK) { 2253 _SCErrorSet(sc_status); 2254 goto done; 2255 } 2256 2257 if (runLoop != NULL) { 2258 connectionPrivate->rls = CFMachPortCreateRunLoopSource(NULL, connectionPrivate->notify_port, 0); 2259 connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 2260 } 2261 } else if (runLoop != NULL) { 2262 CFRunLoopSourceContext rlsContext = { 2263 0, // version 2264 (void *)connection, // info 2265 NULL, // retain 2266 NULL, // release 2267 NULL, // copy description 2268 NULL, // equal 2269 NULL, // hash 2270 NULL, // schedule 2271 NULL, // cancel 2272 __SCNetworkConnectionCallBack, // perform 2273 }; 2274 2275 connectionPrivate->rls = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &rlsContext); 2276 connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 2277 } 2278 2279 connectionPrivate->scheduled = TRUE; 2280 } 2281 2282 if (queue != NULL) { 2283 // retain the dispatch queue 2284 connectionPrivate->dispatchQueue = queue; 2285 dispatch_retain(connectionPrivate->dispatchQueue); 2286 2287 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 2288 dispatch_group_t group = NULL; 2289 mach_port_t mp; 2290 dispatch_source_t source; 2291 2292 // 2293 // We've taken a reference to the caller's dispatch_queue and we 2294 // want to hold on to that reference until we've processed any/all 2295 // notifications. To facilitate this we create a group, dispatch 2296 // any notification blocks via that group, and when the caller 2297 // has told us to stop the notifications (unschedule) we wait for 2298 // the group to empty and use the group's finalizer to release 2299 // our reference to the SCNetworkConnection. 2300 // 2301 group = dispatch_group_create(); 2302 connectionPrivate->dispatchGroup = group; 2303 CFRetain(connection); 2304 dispatch_set_context(connectionPrivate->dispatchGroup, (void *)connection); 2305 dispatch_set_finalizer_f(connectionPrivate->dispatchGroup, (dispatch_function_t)CFRelease); 2306 2307 mp = CFMachPortGetPort(connectionPrivate->notify_port); 2308 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue); 2309 if (source == NULL) { 2310 SC_log(LOG_NOTICE, "dispatch_source_create() failed"); 2311 _SCErrorSet(kSCStatusFailed); 2312 goto done; 2313 } 2314 2315 // have our dispatch source hold a reference to the notification CFMachPort 2316 CFRetain(connectionPrivate->notify_port); 2317 dispatch_set_context(source, (void *)connectionPrivate->notify_port); 2318 dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease); 2319 2320 dispatch_source_set_event_handler(source, ^{ 2321 kern_return_t kr; 2322 typedef union { 2323 u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE]; 2324 mach_msg_empty_rcv_t msg; 2325 mach_no_senders_notification_t no_senders; 2326 } *notify_message_t; 2327 CFMachPortRef notify_port; 2328 notify_message_t notify_msg; 2329 2330 notify_msg = (notify_message_t)malloc(sizeof(*notify_msg)); 2331 2332 kr = mach_msg(&notify_msg->msg.header, // msg 2333 MACH_RCV_MSG, // options 2334 0, // send_size 2335 sizeof(*notify_msg), // rcv_size 2336 mp, // rcv_name 2337 MACH_MSG_TIMEOUT_NONE, // timeout 2338 MACH_PORT_NULL); // notify 2339 if (kr != KERN_SUCCESS) { 2340 SC_log(LOG_NOTICE, "SCDynamicStore notification handler, kr=0x%x", kr); 2341 free(notify_msg); 2342 return; 2343 } 2344 2345 CFRetain(connection); 2346 notify_port = dispatch_get_context(source); 2347 2348 dispatch_group_async(group, queue, ^{ 2349 __SCNetworkConnectionMachCallBack(notify_port, 2350 (void *)notify_msg, 2351 sizeof(*notify_msg), 2352 (void *)connection); 2353 free(notify_msg); 2354 CFRelease(connection); 2355 }); 2356 }); 2357 2358 dispatch_source_set_cancel_handler(source, ^{ 2359 dispatch_release(source); 2360 }); 2361 2362 connectionPrivate->dispatchSource = source; 2363 dispatch_resume(source); 2364 } 2365 } else { 2366 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) { 2367 /* 2368 * if we do not already have notifications scheduled with 2369 * this runLoop / runLoopMode 2370 */ 2371 CFRunLoopAddSource(runLoop, connectionPrivate->rls, runLoopMode); 2372 } 2373 2374 _SC_schedule(connection, runLoop, runLoopMode, connectionPrivate->rlList); 2375 } 2376 2377#if !TARGET_OS_SIMULATOR 2378 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 2379 CFRetain(connection); 2380 ne_session_set_event_handler(connectionPrivate->ne_session, __SCNetworkConnectionQueue(), ^(ne_session_event_t event, void *event_data) { 2381 #pragma unused(event_data) 2382 if (event == NESessionEventStatusChanged) { 2383 CFRetain(connection); /* Released in __SCNetworkConnectionCallBack */ 2384 pthread_mutex_lock(&connectionPrivate->lock); 2385 if (connectionPrivate->rls != NULL) { 2386 CFRunLoopSourceSignal(connectionPrivate->rls); 2387 _SC_signalRunLoop(connection, connectionPrivate->rls, connectionPrivate->rlList); 2388 } else if (connectionPrivate->dispatchQueue != NULL) { 2389 dispatch_async(connectionPrivate->dispatchQueue, ^{ 2390 __SCNetworkConnectionCallBack((void *)connection); 2391 }); 2392 } 2393 pthread_mutex_unlock(&connectionPrivate->lock); 2394 } else if (event == NESessionEventCanceled) { 2395 CFRelease(connection); 2396 } 2397 }); 2398 } 2399#endif /* !TARGET_OS_SIMULATOR */ 2400 2401 ok = TRUE; 2402 2403 done : 2404 2405 pthread_mutex_unlock(&connectionPrivate->lock); 2406 return ok; 2407} 2408 2409 2410static Boolean 2411__SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection, 2412 CFRunLoopRef runLoop, 2413 CFStringRef runLoopMode, 2414 dispatch_queue_t queue) 2415{ 2416#pragma unused(queue) 2417 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 2418 dispatch_group_t drainGroup = NULL; 2419 dispatch_queue_t drainQueue = NULL; 2420 int sc_status = kSCStatusFailed; 2421 CFIndex n = 0; 2422 Boolean ok = FALSE; 2423 kern_return_t status; 2424 2425 // hold a reference while we unschedule 2426 CFRetain(connection); 2427 2428 pthread_mutex_lock(&connectionPrivate->lock); 2429 2430 if ((runLoop != NULL) && !connectionPrivate->scheduled) { // if we should be scheduled (but are not) 2431 _SCErrorSet(kSCStatusInvalidArgument); 2432 goto done; 2433 } 2434 2435 if (((runLoop == NULL) && (connectionPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not) 2436 ((runLoop != NULL) && (connectionPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue) 2437 _SCErrorSet(kSCStatusInvalidArgument); 2438 goto done; 2439 } 2440 2441 if (connectionPrivate->dispatchQueue != NULL) { 2442 if (!__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 2443 // cancel dispatchSource 2444 if (connectionPrivate->dispatchSource != NULL) { 2445 dispatch_source_cancel(connectionPrivate->dispatchSource); 2446 connectionPrivate->dispatchSource = NULL; 2447 } 2448 2449 // save dispatchQueue/group, release reference when all queue'd blocks 2450 // have been processed, allow re-scheduling 2451 drainGroup = connectionPrivate->dispatchGroup; 2452 connectionPrivate->dispatchGroup = NULL; 2453 drainQueue = connectionPrivate->dispatchQueue; 2454 connectionPrivate->dispatchQueue = NULL; 2455 } else { 2456 dispatch_release(connectionPrivate->dispatchQueue); 2457 connectionPrivate->dispatchQueue = NULL; 2458 } 2459 } else { 2460 if (!_SC_unschedule(connection, runLoop, runLoopMode, connectionPrivate->rlList, FALSE)) { 2461 // if not currently scheduled on this runLoop / runLoopMode 2462 _SCErrorSet(kSCStatusFailed); 2463 goto done; 2464 } 2465 2466 n = CFArrayGetCount(connectionPrivate->rlList); 2467 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) { 2468 /* 2469 * if we are no longer scheduled to receive notifications for 2470 * this runLoop / runLoopMode 2471 */ 2472 CFRunLoopRemoveSource(runLoop, connectionPrivate->rls, runLoopMode); 2473 2474 if (n == 0) { 2475 // if *all* notifications have been unscheduled 2476 CFRelease(connectionPrivate->rlList); 2477 connectionPrivate->rlList = NULL; 2478 CFRunLoopSourceInvalidate(connectionPrivate->rls); 2479 CFRelease(connectionPrivate->rls); 2480 connectionPrivate->rls = NULL; 2481 } 2482 } 2483 } 2484 2485 if (n == 0) { 2486 // if *all* notifications have been unscheduled 2487 connectionPrivate->scheduled = FALSE; 2488 2489 if (__SCNetworkConnectionUsingNetworkExtension(connectionPrivate)) { 2490#if !TARGET_OS_SIMULATOR 2491 ne_session_cancel(connectionPrivate->ne_session); 2492#endif /* !TARGET_OS_SIMULATOR */ 2493 } else { 2494 mach_port_t session_port = __SCNetworkConnectionSessionPort(connectionPrivate); 2495 if (session_port == MACH_PORT_NULL) { 2496 goto done; 2497 } 2498 2499 status = pppcontroller_notification(session_port, 0, &sc_status); 2500 if (__SCNetworkConnectionNeedsRetry(connection, 2501 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()", 2502 status, 2503 &sc_status)) { 2504 sc_status = kSCStatusOK; 2505 status = KERN_SUCCESS; 2506 } 2507 2508 if ((status != KERN_SUCCESS) || (sc_status != kSCStatusOK)) { 2509 _SCErrorSet(sc_status); 2510 goto done; 2511 } 2512 } 2513 } 2514 2515 ok = TRUE; 2516 2517 done : 2518 2519 pthread_mutex_unlock(&connectionPrivate->lock); 2520 2521 if (drainGroup != NULL) { 2522 dispatch_group_notify(drainGroup, drainQueue, ^{ 2523 // release group/queue references 2524 dispatch_release(drainQueue); 2525 dispatch_release(drainGroup); // releases our connection reference 2526 }); 2527 } 2528 2529 // release our reference 2530 CFRelease(connection); 2531 2532 return ok; 2533} 2534 2535 2536Boolean 2537SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection, 2538 CFRunLoopRef runLoop, 2539 CFStringRef runLoopMode) 2540{ 2541 if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) { 2542 _SCErrorSet(kSCStatusInvalidArgument); 2543 return FALSE; 2544 } 2545 2546 return __SCNetworkConnectionScheduleWithRunLoop(connection, runLoop, runLoopMode, NULL); 2547} 2548 2549 2550Boolean 2551SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection, 2552 CFRunLoopRef runLoop, 2553 CFStringRef runLoopMode) 2554{ 2555 if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) { 2556 _SCErrorSet(kSCStatusInvalidArgument); 2557 return FALSE; 2558 } 2559 2560 return __SCNetworkConnectionUnscheduleFromRunLoop(connection, runLoop, runLoopMode, NULL); 2561} 2562 2563 2564Boolean 2565SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection, 2566 dispatch_queue_t queue) 2567{ 2568 Boolean ok = FALSE; 2569 2570 if (!isA_SCNetworkConnection(connection)) { 2571 _SCErrorSet(kSCStatusInvalidArgument); 2572 return FALSE; 2573 } 2574 2575 if (queue != NULL) { 2576 ok = __SCNetworkConnectionScheduleWithRunLoop(connection, NULL, NULL, queue); 2577 } else { 2578 ok = __SCNetworkConnectionUnscheduleFromRunLoop(connection, NULL, NULL, NULL); 2579 } 2580 2581 return ok; 2582} 2583 2584 2585/* Requires having called SCNetworkConnectionSelectServiceWithOptions previously */ 2586Boolean 2587SCNetworkConnectionIsOnDemandSuspended(SCNetworkConnectionRef connection) 2588{ 2589 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 2590 2591 if (!isA_SCNetworkConnection(connection)) { 2592 _SCErrorSet(kSCStatusInvalidArgument); 2593 return FALSE; 2594 } 2595 2596 if (connectionPrivate->on_demand_info != NULL) { 2597 uint32_t isSuspended = 0; 2598 CFNumberRef num = NULL; 2599 2600 num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCPropNetVPNOnDemandSuspended); 2601 if (isA_CFNumber(num) && 2602 CFNumberGetValue(num, kCFNumberSInt32Type, &isSuspended) && 2603 (isSuspended != 0)) { 2604 return TRUE; 2605 } 2606 } 2607 2608 _SCErrorSet(kSCStatusOK); 2609 return FALSE; 2610} 2611 2612Boolean 2613SCNetworkConnectionTriggerOnDemandIfNeeded (CFStringRef hostName, 2614 Boolean afterDNSFail, 2615 int timeout, 2616 int trafficClass) 2617{ 2618#if !TARGET_OS_SIMULATOR 2619 __block Boolean triggeredOnDemand = FALSE; 2620 struct proc_uniqidentifierinfo procu; 2621 void *policy_match = NULL; 2622 char *hostname = NULL; 2623 pid_t pid = getpid(); 2624 uid_t uid = geteuid(); 2625 2626 /* Require hostName, require non-root user */ 2627 if (hostName == NULL || geteuid() == 0) { 2628 goto done; 2629 } 2630 2631 hostname = _SC_cfstring_to_cstring(hostName, NULL, 0, kCFStringEncodingUTF8); 2632 2633 if (proc_pidinfo(pid, PROC_PIDUNIQIDENTIFIERINFO, 1, &procu, sizeof(procu)) != sizeof(procu)) { 2634 goto done; 2635 } 2636 2637 policy_match = ne_session_copy_policy_match(hostname, NULL, NULL, procu.p_uuid, procu.p_uuid, pid, uid, 0, trafficClass); 2638 2639 NEPolicyServiceActionType action_type = ne_session_policy_match_get_service_action(policy_match); 2640 if (action_type == NESessionPolicyActionTrigger || 2641 (afterDNSFail && action_type == NESessionPolicyActionTriggerIfNeeded)) { 2642 uuid_t config_id; 2643 if (ne_session_policy_match_get_service(policy_match, config_id)) { 2644 xpc_object_t start_options = xpc_dictionary_create(NULL, NULL, 0); 2645 if (start_options != NULL) { 2646 xpc_dictionary_set_bool(start_options, NESessionStartOptionIsOnDemandKey, true); 2647 xpc_dictionary_set_string(start_options, NESessionStartOptionMatchHostnameKey, hostname); 2648 2649 ne_session_t new_session = ne_session_create(config_id, ne_session_policy_match_get_service_type(policy_match)); 2650 if (new_session != NULL) { 2651 dispatch_semaphore_t wait_for_session = dispatch_semaphore_create(0); 2652 dispatch_retain(wait_for_session); 2653 xpc_retain(start_options); 2654 ne_session_get_status(new_session, __SCNetworkConnectionQueue(), 2655 ^(ne_session_status_t status) { 2656 if (status == NESessionStatusDisconnected) { 2657 dispatch_retain(wait_for_session); 2658 ne_session_set_event_handler(new_session, __SCNetworkConnectionQueue(), 2659 ^(ne_session_event_t event, void *event_data) { 2660#pragma unused(event_data) 2661 if (event == NESessionEventStatusChanged) { 2662 dispatch_retain(wait_for_session); 2663 ne_session_get_status(new_session, __SCNetworkConnectionQueue(), 2664 ^(ne_session_status_t new_status) { 2665 if (new_status != NESessionStatusConnecting) { 2666 if (status == NESessionStatusConnected) { 2667 triggeredOnDemand = TRUE; 2668 } 2669 ne_session_cancel(new_session); 2670 } 2671 dispatch_release(wait_for_session); 2672 }); 2673 } else if (event == NESessionEventCanceled) { 2674 dispatch_semaphore_signal(wait_for_session); 2675 dispatch_release(wait_for_session); 2676 } 2677 }); 2678 ne_session_start_with_options(new_session, start_options); 2679 } else { 2680 dispatch_semaphore_signal(wait_for_session); 2681 } 2682 dispatch_release(wait_for_session); 2683 xpc_release(start_options); 2684 }); 2685 dispatch_semaphore_wait(wait_for_session, timeout ? dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout * NSEC_PER_SEC) : DISPATCH_TIME_FOREVER); 2686 dispatch_release(wait_for_session); 2687 ne_session_release(new_session); 2688 } 2689 2690 xpc_release(start_options); 2691 } 2692 } 2693 } 2694done: 2695 if (hostname) { 2696 CFAllocatorDeallocate(NULL, hostname); 2697 } 2698 2699 if (policy_match) { 2700 free(policy_match); 2701 } 2702 2703 return triggeredOnDemand; 2704#else 2705#pragma unused(hostName, afterDNSFail, timeout, trafficClass) 2706 return FALSE; 2707#endif 2708} 2709 2710 2711Boolean 2712SCNetworkConnectionCopyOnDemandInfo(SCNetworkConnectionRef connection, 2713 CFStringRef *onDemandRemoteAddress, 2714 SCNetworkConnectionStatus *onDemandConnectionStatus) 2715{ 2716 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 2717 2718 if (!isA_SCNetworkConnection(connection)) { 2719 _SCErrorSet(kSCStatusInvalidArgument); 2720 return FALSE; 2721 } 2722 2723 if (connectionPrivate->service == NULL) { 2724 _SCErrorSet(kSCStatusConnectionNoService); 2725 return FALSE; 2726 } 2727 2728 if (onDemandRemoteAddress != NULL) { 2729 *onDemandRemoteAddress = NULL; 2730 } 2731 2732 if (onDemandConnectionStatus != NULL) { 2733 *onDemandConnectionStatus = kSCNetworkConnectionInvalid; 2734 } 2735 2736 if (connectionPrivate->on_demand_info != NULL) { 2737 if (onDemandRemoteAddress != NULL) { 2738 CFStringRef address = 2739 CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandRemoteAddress); 2740 if (isA_CFString(address)) { 2741 *onDemandRemoteAddress = address; 2742 CFRetain(*onDemandRemoteAddress); 2743 } 2744 } 2745 2746 if (onDemandConnectionStatus != NULL) { 2747 int num; 2748 CFNumberRef status_num = 2749 CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandStatus); 2750 if (isA_CFNumber(status_num) && CFNumberGetValue(status_num, kCFNumberIntType, &num)) { 2751 *onDemandConnectionStatus = num; 2752 } 2753 } 2754 } 2755 2756 return connectionPrivate->on_demand; 2757} 2758 2759 2760Boolean 2761SCNetworkConnectionGetReachabilityInfo(SCNetworkConnectionRef connection, 2762 SCNetworkReachabilityFlags *reach_flags, 2763 unsigned int *reach_if_index) 2764{ 2765 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 2766 2767 if (!isA_SCNetworkConnection(connection)) { 2768 _SCErrorSet(kSCStatusInvalidArgument); 2769 return FALSE; 2770 } 2771 2772 if (connectionPrivate->service == NULL) { 2773 _SCErrorSet(kSCStatusConnectionNoService); 2774 return FALSE; 2775 } 2776 2777 if (reach_flags != NULL) { 2778 *reach_flags = 0; 2779 } 2780 2781 if (reach_if_index != NULL) { 2782 *reach_if_index = 0; 2783 } 2784 2785 if (connectionPrivate->on_demand_info != NULL) { 2786 if (reach_flags != NULL) { 2787 int num; 2788 CFNumberRef flags_num = 2789 CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandReachFlags); 2790 if (isA_CFNumber(flags_num) && CFNumberGetValue(flags_num, kCFNumberIntType, &num)) { 2791 *reach_flags = num; 2792 } 2793 } 2794 2795 if (reach_if_index != NULL) { 2796 int num; 2797 CFNumberRef if_index_num = 2798 CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCNetworkConnectionOnDemandReachInterfaceIndex); 2799 if (isA_CFNumber(if_index_num) && CFNumberGetValue(if_index_num, kCFNumberIntType, &num)) { 2800 *reach_if_index = num; 2801 } 2802 } 2803 } 2804 2805 return TRUE; 2806} 2807 2808 2809SCNetworkConnectionType 2810SCNetworkConnectionGetType(SCNetworkConnectionRef connection) 2811{ 2812 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 2813 2814 if (!isA_SCNetworkConnection(connection)) { 2815 _SCErrorSet(kSCStatusInvalidArgument); 2816 return kSCNetworkConnectionTypeUnknown; 2817 } 2818 2819 if (connectionPrivate->service == NULL) { 2820 _SCErrorSet(kSCStatusConnectionNoService); 2821 return kSCNetworkConnectionTypeUnknown; 2822 } 2823 2824 _SCErrorSet(kSCStatusOK); 2825 2826 return connectionPrivate->type; 2827} 2828 2829 2830CFDataRef 2831SCNetworkConnectionCopyFlowDivertToken(SCNetworkConnectionRef connection, 2832 CFDictionaryRef flowProperties) 2833{ 2834#pragma unused(connection, flowProperties) 2835 _SCErrorSet(kSCStatusFailed); 2836 return NULL; 2837} 2838 2839 2840int 2841SCNetworkConnectionGetServiceIdentifier (SCNetworkConnectionRef connection) 2842{ 2843 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 2844 int service_identifier = -1; 2845 2846 if (connectionPrivate->service != NULL) { 2847 service_identifier = 0; 2848 if (connectionPrivate->on_demand_info != NULL) { 2849 CFNumberRef id_num = CFDictionaryGetValue(connectionPrivate->on_demand_info, kSCPropNetDNSServiceIdentifier); 2850 2851 if (isA_CFNumber(id_num)) { 2852 CFNumberGetValue(id_num, kCFNumberIntType, &service_identifier); 2853 } 2854 } 2855 } 2856 2857 return service_identifier; 2858} 2859 2860 2861#if !TARGET_OS_SIMULATOR 2862SCNetworkConnectionStatus 2863SCNetworkConnectionGetStatusFromNEStatus(ne_session_status_t status) 2864{ 2865 switch (status) { 2866 case NESessionStatusInvalid: 2867 return kSCNetworkConnectionInvalid; 2868 case NESessionStatusDisconnected: 2869 return kSCNetworkConnectionDisconnected; 2870 case NESessionStatusConnecting: 2871 case NESessionStatusReasserting: 2872 return kSCNetworkConnectionConnecting; 2873 case NESessionStatusConnected: 2874 return kSCNetworkConnectionConnected; 2875 case NESessionStatusDisconnecting: 2876 return kSCNetworkConnectionDisconnecting; 2877 } 2878 2879 return kSCNetworkConnectionInvalid; 2880} 2881#endif /* !TARGET_OS_SIMULATOR */ 2882 2883 2884#pragma mark - 2885#pragma mark User level "dial" API 2886 2887 2888#define k_NetworkConnect_Notification "com.apple.networkConnect" 2889#define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect") 2890#define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect") 2891 2892#define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC 2893#define k_Last_Service_Id_Key CFSTR("ServiceID") 2894#define k_Unique_Id_Key CFSTR("UniqueIdentifier") 2895 2896 2897/* Private Prototypes */ 2898static Boolean SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (CFStringRef *serviceID); 2899static Boolean SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (CFStringRef *serviceID); 2900static Boolean SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions); 2901static Boolean SCNetworkConnectionPrivateIsPPPService (CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2); 2902static void addPasswordFromKeychain (CFStringRef serviceID, CFDictionaryRef *userOptions); 2903static CFStringRef copyPasswordFromKeychain (CFStringRef uniqueID); 2904 2905static int notify_userprefs_token = -1; 2906 2907static CFDictionaryRef onDemand_configuration = NULL; 2908static Boolean onDemand_force_refresh = FALSE; 2909static pthread_mutex_t onDemand_notify_lock = PTHREAD_MUTEX_INITIALIZER; 2910static int onDemand_notify_token = -1; 2911 2912 2913/* 2914 * return TRUE if domain1 ends with domain2, and will check for trailing "." 2915 */ 2916#define WILD_CARD_MATCH_STR CFSTR("*") 2917Boolean 2918_SC_domainEndsWithDomain(CFStringRef compare_domain, CFStringRef match_domain) 2919{ 2920 CFRange range; 2921 Boolean ret = FALSE; 2922 CFStringRef s1 = NULL; 2923 Boolean s1_created = FALSE; 2924 CFStringRef s2 = NULL; 2925 Boolean s2_created = FALSE; 2926 CFStringRef s3 = NULL; 2927 2928 if (CFEqual(match_domain, WILD_CARD_MATCH_STR)) { 2929 return TRUE; 2930 } 2931 2932 if (CFStringHasSuffix(compare_domain, CFSTR("."))) { 2933 range.location = 0; 2934 range.length = CFStringGetLength(compare_domain) - 1; 2935 s1 = CFStringCreateWithSubstring(NULL, compare_domain, range); 2936 if (s1 == NULL) { 2937 goto done; 2938 } 2939 s1_created = TRUE; 2940 } else { 2941 s1 = compare_domain; 2942 } 2943 2944 if (CFStringHasSuffix(match_domain, CFSTR("."))) { 2945 range.location = 0; 2946 range.length = CFStringGetLength(match_domain) - 1; 2947 s2 = CFStringCreateWithSubstring(NULL, match_domain, range); 2948 if (s2 == NULL) { 2949 goto done; 2950 } 2951 s2_created = TRUE; 2952 } else { 2953 s2 = match_domain; 2954 } 2955 2956 if (CFStringHasPrefix(s2, CFSTR("*."))) { 2957 range.location = 2; 2958 range.length = CFStringGetLength(s2)-2; 2959 s3 = CFStringCreateWithSubstring(NULL, s2, range); 2960 if (s3 == NULL) { 2961 goto done; 2962 } 2963 if (s2_created) { 2964 CFRelease(s2); 2965 } 2966 s2 = s3; 2967 s2_created = TRUE; 2968 } 2969 2970 ret = CFStringHasSuffix(s1, s2); 2971 2972 done : 2973 2974 if (s1_created) CFRelease(s1); 2975 if (s2_created) CFRelease(s2); 2976 return ret; 2977} 2978 2979static CFCharacterSetRef 2980_SC_getNotDotOrStarCharacterSet (void) 2981{ 2982 static CFCharacterSetRef notDotOrStar = NULL; 2983 if (notDotOrStar == NULL) { 2984 CFCharacterSetRef dotOrStar = CFCharacterSetCreateWithCharactersInString(kCFAllocatorDefault, CFSTR(".*")); 2985 if (dotOrStar) { 2986 notDotOrStar = CFCharacterSetCreateInvertedSet(kCFAllocatorDefault, dotOrStar); 2987 CFRelease(dotOrStar); 2988 } 2989 } 2990 return notDotOrStar; 2991} 2992 2993static CFMutableStringRef 2994_SC_createStringByTrimmingDotsAndStars (CFStringRef string) 2995{ 2996 CFCharacterSetRef notDotOrStar = _SC_getNotDotOrStarCharacterSet(); 2997 CFRange entireString = CFRangeMake(0, CFStringGetLength(string)); 2998 CFMutableStringRef result = CFStringCreateMutableCopy(kCFAllocatorDefault, entireString.length, string); 2999 CFRange start; 3000 CFRange end = CFRangeMake(entireString.length, 0); 3001 3002 if (CFStringFindCharacterFromSet(string, notDotOrStar, entireString, 0, &start) && 3003 CFStringFindCharacterFromSet(string, notDotOrStar, entireString, kCFCompareBackwards, &end)) { 3004 if (start.location == kCFNotFound || end.location == kCFNotFound || start.location > end.location) { 3005 CFRelease(result); 3006 return NULL; 3007 } 3008 } 3009 3010 if ((end.location + 1) < entireString.length) { 3011 CFStringReplace(result, CFRangeMake(end.location + 1, entireString.length - (end.location + 1)), CFSTR("")); 3012 } 3013 if (start.location > 0) { 3014 CFStringReplace(result, CFRangeMake(0, start.location), CFSTR("")); 3015 } 3016 3017 return result; 3018} 3019 3020static CFIndex 3021_SC_getCountOfStringInString (CFStringRef string, CFStringRef substring) 3022{ 3023 CFIndex count = 0; 3024 CFArrayRef ranges = CFStringCreateArrayWithFindResults(kCFAllocatorDefault, string, substring, CFRangeMake(0, CFStringGetLength(string)), 0); 3025 if (ranges != NULL) { 3026 count = CFArrayGetCount(ranges); 3027 CFRelease(ranges); 3028 } 3029 return count; 3030} 3031 3032Boolean 3033_SC_hostMatchesDomain(CFStringRef hostname, CFStringRef domain) 3034{ 3035 Boolean result = FALSE; 3036 CFMutableStringRef trimmedHostname = NULL; 3037 CFMutableStringRef trimmedDomain = NULL; 3038 3039 if (!isA_CFString(hostname) || !isA_CFString(domain)) { 3040 goto done; 3041 } 3042 3043 trimmedHostname = _SC_createStringByTrimmingDotsAndStars(hostname); 3044 trimmedDomain = _SC_createStringByTrimmingDotsAndStars(domain); 3045 3046 if (!isA_CFString(trimmedHostname) || !isA_CFString(trimmedDomain)) { 3047 goto done; 3048 } 3049 3050 CFIndex numHostnameDots = _SC_getCountOfStringInString(trimmedHostname, CFSTR(".")); 3051 CFIndex numDomainDots = _SC_getCountOfStringInString(trimmedDomain, CFSTR(".")); 3052 if (numHostnameDots == numDomainDots) { 3053 result = CFEqual(trimmedHostname, trimmedDomain); 3054 } else if (numDomainDots > 0 && numDomainDots < numHostnameDots) { 3055 CFStringReplace(trimmedDomain, CFRangeMake(0, 0), CFSTR(".")); 3056 result = CFStringHasSuffix(trimmedHostname, trimmedDomain); 3057 } else { 3058 result = FALSE; 3059 } 3060 3061done: 3062 if (trimmedHostname) { 3063 CFRelease(trimmedHostname); 3064 } 3065 if (trimmedDomain) { 3066 CFRelease(trimmedDomain); 3067 } 3068 return result; 3069} 3070 3071/* VPN On Demand */ 3072 3073static CFDictionaryRef 3074__SCNetworkConnectionCopyOnDemandConfiguration(void) 3075{ 3076 int changed = 1; 3077 int status; 3078 uint64_t triggersCount = 0; 3079 CFDictionaryRef configuration; 3080 3081 pthread_mutex_lock(&onDemand_notify_lock); 3082 if (onDemand_notify_token == -1) { 3083 status = notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY, &onDemand_notify_token); 3084 if (status != NOTIFY_STATUS_OK) { 3085 SC_log(LOG_NOTICE, "notify_register_check() failed, status=%d", status); 3086 onDemand_notify_token = -1; 3087 } 3088 } 3089 3090 if (onDemand_notify_token != -1) { 3091 status = notify_check(onDemand_notify_token, &changed); 3092 if (status != NOTIFY_STATUS_OK) { 3093 SC_log(LOG_NOTICE, "notify_check() failed, status=%d", status); 3094 (void)notify_cancel(onDemand_notify_token); 3095 onDemand_notify_token = -1; 3096 } 3097 } 3098 3099 if (changed && (onDemand_notify_token != -1)) { 3100 status = notify_get_state(onDemand_notify_token, &triggersCount); 3101 if (status != NOTIFY_STATUS_OK) { 3102 SC_log(LOG_NOTICE, "notify_get_state() failed, status=%d", status); 3103 (void)notify_cancel(onDemand_notify_token); 3104 onDemand_notify_token = -1; 3105 } 3106 } 3107 3108 if (changed || onDemand_force_refresh) { 3109 CFStringRef key; 3110 3111 SC_log(LOG_INFO, "OnDemand information %s", 3112 (onDemand_configuration == NULL) ? "fetched" : "updated"); 3113 3114 if (onDemand_configuration != NULL) { 3115 CFRelease(onDemand_configuration); 3116 onDemand_configuration = NULL; 3117 } 3118 3119 if ((triggersCount > 0) || onDemand_force_refresh) { 3120 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand); 3121 onDemand_configuration = SCDynamicStoreCopyValue(NULL, key); 3122 CFRelease(key); 3123 if ((onDemand_configuration != NULL) && !isA_CFDictionary(onDemand_configuration)) { 3124 CFRelease(onDemand_configuration); 3125 onDemand_configuration = NULL; 3126 } 3127 } 3128 3129 onDemand_force_refresh = FALSE; 3130 } 3131 3132 configuration = (onDemand_configuration != NULL) ? CFRetain(onDemand_configuration) : NULL; 3133 pthread_mutex_unlock(&onDemand_notify_lock); 3134 3135 return configuration; 3136} 3137 3138 3139__private_extern__ 3140void 3141__SCNetworkConnectionForceOnDemandConfigurationRefresh(void) 3142{ 3143 pthread_mutex_lock(&onDemand_notify_lock); 3144 onDemand_force_refresh = TRUE; 3145 pthread_mutex_unlock(&onDemand_notify_lock); 3146 3147 return; 3148} 3149 3150 3151static Boolean 3152__SCNetworkConnectionShouldNeverMatch(CFDictionaryRef trigger, CFStringRef hostName, pid_t client_pid) 3153{ 3154 CFArrayRef exceptedProcesses; 3155 CFIndex exceptedProcessesCount; 3156 CFIndex exceptedProcessesIndex; 3157 CFArrayRef exceptions; 3158 CFIndex exceptionsCount; 3159 int exceptionsIndex; 3160 3161 // we have a matching domain, check against exception list 3162 exceptions = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandMatchDomainsNever); 3163 exceptionsCount = isA_CFArray(exceptions) ? CFArrayGetCount(exceptions) : 0; 3164 for (exceptionsIndex = 0; exceptionsIndex < exceptionsCount; exceptionsIndex++) { 3165 CFStringRef exception; 3166 3167 exception = CFArrayGetValueAtIndex(exceptions, exceptionsIndex); 3168 if (isA_CFString(exception) && _SC_domainEndsWithDomain(hostName, exception)) { 3169 // found matching exception 3170 SC_log(LOG_INFO, "OnDemand match exception"); 3171 return TRUE; 3172 } 3173 } 3174 3175 if (client_pid != 0) { 3176 exceptedProcesses = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandPluginPIDs); 3177 exceptedProcessesCount = isA_CFArray(exceptedProcesses) ? CFArrayGetCount(exceptedProcesses) : 0; 3178 for (exceptedProcessesIndex = 0; exceptedProcessesIndex < exceptedProcessesCount; exceptedProcessesIndex++) { 3179 int pid; 3180 CFNumberRef pidRef; 3181 3182 pidRef = CFArrayGetValueAtIndex(exceptedProcesses, exceptedProcessesIndex); 3183 if (isA_CFNumber(pidRef) && CFNumberGetValue(pidRef, kCFNumberIntType, &pid)) { 3184 if (pid == client_pid) { 3185 return TRUE; 3186 } 3187 } 3188 } 3189 } 3190 3191 return FALSE; 3192} 3193 3194static CFStringRef 3195__SCNetworkConnectionDomainGetMatchWithParameters(CFStringRef action, CFPropertyListRef actionParameters, CFStringRef hostName, CFStringRef *probeString) 3196{ 3197 CFArrayRef actionArray = NULL; 3198 CFIndex actionArraySize = 0; 3199 CFIndex i; 3200 CFStringRef matchDomain = NULL; 3201 3202 /* For now, only support EvaluateConnection, which takes a CFArray */ 3203 if (!CFEqual(action, kSCValNetVPNOnDemandRuleActionEvaluateConnection) || !isA_CFArray(actionParameters)) { 3204 return NULL; 3205 } 3206 3207 actionArray = (CFArrayRef)actionParameters; 3208 actionArraySize = CFArrayGetCount(actionArray); 3209 3210 /* Process domain rules, with actions of ConnectIfNeeded and NeverConnect */ 3211 for (i = 0; i < actionArraySize; i++) { 3212 CFStringRef domainAction = NULL; 3213 CFDictionaryRef domainRule = CFArrayGetValueAtIndex(actionArray, i); 3214 CFArrayRef domains = NULL; 3215 CFIndex domainsCount = 0; 3216 CFIndex domainsIndex; 3217 3218 if (!isA_CFDictionary(domainRule)) { 3219 continue; 3220 } 3221 3222 domains = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomains); 3223 if (!isA_CFArray(domains)) { 3224 continue; 3225 } 3226 3227 domainsCount = CFArrayGetCount(domains); 3228 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) { 3229 CFStringRef domain; 3230 domain = CFArrayGetValueAtIndex(domains, domainsIndex); 3231 if (isA_CFString(domain) && _SC_domainEndsWithDomain(hostName, domain)) { 3232 matchDomain = domain; 3233 break; 3234 } 3235 } 3236 3237 if (matchDomain) { 3238 domainAction = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomainAction); 3239 if (isA_CFString(domainAction) && CFEqual(domainAction, kSCValNetVPNOnDemandRuleActionParametersDomainActionNeverConnect)) { 3240 return NULL; 3241 } else { 3242 /* If we found a match, save the optional probe string as well */ 3243 if (probeString) { 3244 *probeString = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe); 3245 } 3246 break; 3247 } 3248 } 3249 } 3250 3251 return matchDomain; 3252} 3253 3254static CFStringRef 3255__SCNetworkConnectionDomainGetMatch(CFDictionaryRef trigger, CFStringRef hostName, Boolean onDemandRetry) 3256{ 3257 CFArrayRef domains; 3258 CFIndex domainsCount; 3259 int domainsIndex; 3260 CFStringRef key; 3261 CFStringRef match_domain = NULL; 3262 3263 /* Old configuration: always, never, on retry lists */ 3264 key = onDemandRetry ? kSCNetworkConnectionOnDemandMatchDomainsOnRetry : kSCNetworkConnectionOnDemandMatchDomainsAlways; 3265 3266 domains = CFDictionaryGetValue(trigger, key); 3267 domainsCount = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0; 3268 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) { 3269 CFStringRef domain; 3270 3271 domain = CFArrayGetValueAtIndex(domains, domainsIndex); 3272 if (isA_CFString(domain) && _SC_domainEndsWithDomain(hostName, domain)) { 3273 match_domain = domain; 3274 break; 3275 } 3276 } 3277 3278 return match_domain; 3279} 3280 3281 3282static Boolean 3283__SCNetworkConnectionShouldAlwaysConnect(CFDictionaryRef trigger) 3284{ 3285 CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction); 3286 return (isA_CFString(action) && CFEqual(action, kSCValNetVPNOnDemandRuleActionConnect)); 3287} 3288 3289 3290static Boolean 3291__SCNetworkConnectionShouldIgnoreTrigger(CFDictionaryRef trigger) 3292{ 3293 CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction); 3294 3295 if (isA_CFString(action) && 3296 (CFEqual(action, kSCValNetVPNOnDemandRuleActionIgnore) || 3297 CFEqual(action, kSCValNetVPNOnDemandRuleActionDisconnect))) { 3298 return TRUE; 3299 } 3300 3301 return FALSE; 3302} 3303 3304 3305static CFDictionaryRef 3306__SCNetworkConnectionCopyMatchingTriggerWithName(CFDictionaryRef configuration, 3307 CFStringRef hostName, 3308 pid_t client_pid, 3309 Boolean onDemandRetry, 3310 CFDictionaryRef *match_info, 3311 Boolean *triggerNow, 3312 CFStringRef *probe_string) 3313{ 3314 CFDictionaryRef result = NULL; 3315 int sc_status = kSCStatusOK; 3316 CFArrayRef triggers; 3317 CFIndex triggersCount = 0; 3318 Boolean usedOnDemandRetry = FALSE; 3319 3320 if (triggerNow != NULL) { 3321 *triggerNow = FALSE; 3322 } 3323 3324 if (match_info != NULL) { 3325 *match_info = NULL; 3326 } 3327 3328 triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers); 3329 triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0; 3330 for (CFIndex triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) { 3331 CFStringRef matched_domain = NULL; 3332 CFStringRef matched_probe_string = NULL; 3333 CFDictionaryRef trigger; 3334 Boolean trigger_matched = FALSE; 3335 3336 usedOnDemandRetry = FALSE; 3337 3338 trigger = CFArrayGetValueAtIndex(triggers, triggersIndex); 3339 if (!isA_CFDictionary(trigger)) { 3340 // if not a valid "OnDemand" configuration 3341 continue; 3342 } 3343 3344 if (__SCNetworkConnectionShouldAlwaysConnect(trigger)) { 3345 /* If the trigger action is 'Connect', always match this trigger */ 3346 /* First check the never match list */ 3347 if (__SCNetworkConnectionShouldNeverMatch(trigger, hostName, client_pid)) { 3348 continue; 3349 } 3350 trigger_matched = TRUE; 3351 } else if (__SCNetworkConnectionShouldIgnoreTrigger(trigger)) { 3352 /* If the trigger action is 'Ignore' or 'Disconnect', skip this trigger */ 3353 sc_status = kSCStatusConnectionIgnore; 3354 continue; 3355 } else { 3356 CFStringRef action = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleAction); 3357 CFArrayRef actionParameters = CFDictionaryGetValue(trigger, kSCPropNetVPNOnDemandRuleActionParameters); 3358 if (action && actionParameters) { 3359 matched_domain = __SCNetworkConnectionDomainGetMatchWithParameters(action, actionParameters, hostName, &matched_probe_string); 3360 usedOnDemandRetry = TRUE; 3361 } else { 3362 if (onDemandRetry) { 3363 matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, TRUE); 3364 usedOnDemandRetry = TRUE; 3365 } else { 3366 matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, FALSE); 3367 if (matched_domain == NULL && result == NULL) { 3368 /* Check the retry list if Always failed */ 3369 matched_domain = __SCNetworkConnectionDomainGetMatch(trigger, hostName, TRUE); 3370 usedOnDemandRetry = TRUE; 3371 } 3372 } 3373 } 3374 3375 if (matched_domain) { 3376 if (__SCNetworkConnectionShouldNeverMatch(trigger, hostName, client_pid)) { 3377 matched_domain = NULL; 3378 continue; 3379 } else { 3380 trigger_matched = TRUE; 3381 } 3382 } 3383 } 3384 3385 if (trigger_matched) { 3386 // if we have a matching domain and there were no exceptions 3387 // then we pass back the OnDemand info 3388 if (match_info != NULL) { 3389 CFMutableDictionaryRef minfo; 3390 SCNetworkConnectionType type = kSCNetworkConnectionTypeIPLayerVPN; 3391 CFNumberRef type_num; 3392 3393 if (*match_info != NULL) { 3394 CFRelease(*match_info); 3395 *match_info = NULL; 3396 } 3397 3398 minfo = CFDictionaryCreateMutable(kCFAllocatorDefault, 3399 0, 3400 &kCFTypeDictionaryKeyCallBacks, 3401 &kCFTypeDictionaryValueCallBacks); 3402 3403 type_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &type); 3404 CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoVPNType, type_num); 3405 CFRelease(type_num); 3406 if (matched_domain) { 3407 CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoDomain, matched_domain); 3408 } 3409 CFDictionarySetValue(minfo, 3410 kSCNetworkConnectionOnDemandMatchInfoOnRetry, 3411 (usedOnDemandRetry ? kCFBooleanTrue : kCFBooleanFalse)); 3412 3413 *match_info = minfo; 3414 } 3415 3416 if (probe_string != NULL) { 3417 if (*probe_string != NULL) { 3418 CFRelease(*probe_string); 3419 *probe_string = NULL; 3420 } 3421 3422 if (matched_probe_string) { 3423 *probe_string = CFRetain(matched_probe_string); 3424 } 3425 } 3426 3427 result = trigger; 3428 3429 /* If retry was requested, or we found Always match, trigger now */ 3430 if (onDemandRetry || !usedOnDemandRetry) { 3431 if (triggerNow != NULL) { 3432 *triggerNow = TRUE; 3433 } 3434 break; 3435 } 3436 3437 /* If we matched the Retry list, but Always was requested, 3438 keep going through triggers in case one matches an Always */ 3439 } 3440 } 3441 3442 if (result) { 3443 CFRetain(result); 3444 } 3445 3446 _SCErrorSet(sc_status); 3447 return result; 3448} 3449 3450 3451static CFDictionaryRef 3452__SCNetworkConnectionCopyTriggerWithService(CFDictionaryRef configuration, 3453 CFStringRef service_id) 3454{ 3455 CFArrayRef triggers; 3456 CFIndex triggersCount; 3457 3458 triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers); 3459 triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0; 3460 for (CFIndex triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) { 3461 CFDictionaryRef trigger; 3462 CFStringRef trigger_service_id; 3463 3464 trigger = CFArrayGetValueAtIndex(triggers, triggersIndex); 3465 if (!isA_CFDictionary(trigger)) { 3466 // if not a valid "OnDemand" configuration 3467 continue; 3468 } 3469 3470 trigger_service_id = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID); 3471 if (isA_CFString(trigger_service_id) && CFEqual(trigger_service_id, service_id)) { 3472 CFRetain(trigger); 3473 return trigger; 3474 } 3475 } 3476 3477 return NULL; 3478} 3479 3480 3481Boolean 3482__SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef *storeP, 3483 CFStringRef hostName, 3484 Boolean onDemandRetry, 3485 CFStringRef *connectionServiceID, 3486 SCNetworkConnectionStatus *connectionStatus, 3487 CFStringRef *vpnRemoteAddress) /* CFDictionaryRef *info */ 3488{ 3489#pragma unused(storeP) 3490 CFDictionaryRef configuration; 3491 Boolean ok = FALSE; 3492 int sc_status = kSCStatusOK; 3493 CFDictionaryRef trigger; 3494 Boolean trigger_now = FALSE; 3495 3496 configuration = __SCNetworkConnectionCopyOnDemandConfiguration(); 3497 if (configuration == NULL) { 3498 _SCErrorSet(sc_status); 3499 return ok; 3500 } 3501 3502 trigger = __SCNetworkConnectionCopyMatchingTriggerWithName(configuration, hostName, 0, onDemandRetry, NULL, &trigger_now, NULL); 3503 if (trigger != NULL && trigger_now) { 3504 CFNumberRef num; 3505 SCNetworkConnectionStatus onDemandStatus = kSCNetworkConnectionDisconnected; 3506 3507 ok = TRUE; 3508 3509 if (!CFDictionaryGetValueIfPresent(trigger, kSCNetworkConnectionOnDemandStatus, (const void **)&num) || 3510 !isA_CFNumber(num) || 3511 !CFNumberGetValue(num, kCFNumberSInt32Type, &onDemandStatus)) { 3512 onDemandStatus = kSCNetworkConnectionDisconnected; 3513 } 3514 if (connectionStatus != NULL) { 3515 *connectionStatus = onDemandStatus; 3516 } 3517 3518 if (connectionServiceID != NULL) { 3519 *connectionServiceID = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID); 3520 *connectionServiceID = isA_CFString(*connectionServiceID); 3521 if ((*connectionServiceID != NULL) && (CFStringGetLength(*connectionServiceID) > 0)) { 3522 CFRetain(*connectionServiceID); 3523 } else { 3524 SC_log(LOG_INFO, "OnDemand%s configuration error, no serviceID", 3525 onDemandRetry ? " (on retry)" : ""); 3526 *connectionServiceID = NULL; 3527 ok = FALSE; 3528 } 3529 } 3530 3531 if (vpnRemoteAddress != NULL) { 3532 *vpnRemoteAddress = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandRemoteAddress); 3533 *vpnRemoteAddress = isA_CFString(*vpnRemoteAddress); 3534 if ((*vpnRemoteAddress != NULL) && (CFStringGetLength(*vpnRemoteAddress) > 0)) { 3535 CFRetain(*vpnRemoteAddress); 3536 } else { 3537 SC_log(LOG_INFO, "OnDemand%s configuration error, no server address", 3538 onDemandRetry ? " (on retry)" : ""); 3539 *vpnRemoteAddress = NULL; 3540 ok = FALSE; 3541 } 3542 } 3543 3544 if (!ok) { 3545 if ((connectionServiceID != NULL) && (*connectionServiceID != NULL)) { 3546 CFRelease(*connectionServiceID); 3547 *connectionServiceID = NULL; 3548 } 3549 if ((vpnRemoteAddress != NULL) && (*vpnRemoteAddress != NULL)) { 3550 CFRelease(*vpnRemoteAddress); 3551 *vpnRemoteAddress = NULL; 3552 } 3553 sc_status = kSCStatusFailed; 3554 } else { 3555 SC_log(LOG_INFO, "OnDemand%s match, connection status = %d", 3556 onDemandRetry ? " (on retry)" : "", 3557 onDemandStatus); 3558 } 3559 } 3560 3561 if (trigger) { 3562 CFRelease(trigger); 3563 } 3564 3565// SC_log(LOG_INFO, "OnDemand domain name(s) not matched"); 3566 3567 if (configuration != NULL) CFRelease(configuration); 3568 if (!ok) { 3569 _SCErrorSet(sc_status); 3570 } 3571 return ok; 3572} 3573 3574static Boolean 3575__SCNetworkConnectionCopyUserPreferencesInternal(CFDictionaryRef selectionOptions, 3576 CFStringRef *serviceID, 3577 CFDictionaryRef *userOptions) 3578{ 3579 int prefsChanged = 1; 3580 int status; 3581 Boolean success = FALSE; 3582 3583 if (notify_userprefs_token == -1) { 3584 status = notify_register_check(k_NetworkConnect_Notification, &notify_userprefs_token); 3585 if (status != NOTIFY_STATUS_OK) { 3586 SC_log(LOG_NOTICE, "notify_register_check() failed, status=%d", status); 3587 (void)notify_cancel(notify_userprefs_token); 3588 notify_userprefs_token = -1; 3589 } else { 3590 // clear the "something has changed" state 3591 (void) notify_check(notify_userprefs_token, &prefsChanged); 3592 prefsChanged = 1; 3593 } 3594 } 3595 if (notify_userprefs_token != -1) { 3596 status = notify_check(notify_userprefs_token, &prefsChanged); 3597 if (status != NOTIFY_STATUS_OK) { 3598 SC_log(LOG_NOTICE, "notify_check() failed, status=%d", status); 3599 (void)notify_cancel(notify_userprefs_token); 3600 notify_userprefs_token = -1; 3601 } 3602 } 3603 3604 3605 *serviceID = NULL; 3606 *userOptions = NULL; 3607 3608 if (selectionOptions != NULL) { 3609 Boolean catchAllFound = FALSE; 3610 CFIndex catchAllService = 0; 3611 CFIndex catchAllConfig = 0; 3612 CFStringRef hostName = NULL; 3613 CFStringRef priority = NULL; 3614 CFArrayRef serviceNames = NULL; 3615 CFDictionaryRef services = NULL; 3616 CFIndex serviceIndex; 3617 CFIndex servicesCount; 3618 3619 hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName); 3620 if (hostName == NULL) { 3621 hostName = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandHostName); 3622 } 3623 hostName = isA_CFString(hostName); 3624 if (hostName == NULL) 3625 goto done_selection; // if no hostname for matching 3626 3627 priority = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandPriority); 3628 if (!isA_CFString(priority)) 3629 priority = kSCValNetPPPOnDemandPriorityDefault; 3630 3631 3632 if (!isA_CFArray(serviceNames)) 3633 goto done_selection; 3634 3635 3636 if (!isA_CFDictionary(services)) { 3637 goto done_selection; 3638 } 3639 3640 servicesCount = CFArrayGetCount(serviceNames); 3641 for (serviceIndex = 0; serviceIndex < servicesCount; serviceIndex++) { 3642 CFIndex configIndex; 3643 CFIndex configsCount; 3644 CFArrayRef serviceConfigs; 3645 CFStringRef serviceName; 3646 int val; 3647 3648 serviceName = CFArrayGetValueAtIndex(serviceNames, serviceIndex); 3649 if (!isA_CFString(serviceName)) { 3650 continue; 3651 } 3652 3653 serviceConfigs = CFDictionaryGetValue(services, serviceName); 3654 if (!isA_CFArray(serviceConfigs)) { 3655 continue; 3656 } 3657 3658 configsCount = CFArrayGetCount(serviceConfigs); 3659 for (configIndex = 0; configIndex < configsCount; configIndex++) { 3660 CFNumberRef autodial; 3661 CFDictionaryRef config; 3662 CFDictionaryRef pppConfig; 3663 3664 config = CFArrayGetValueAtIndex(serviceConfigs, configIndex); 3665 if (!isA_CFDictionary(config)) { 3666 continue; 3667 } 3668 3669 pppConfig = CFDictionaryGetValue(config, kSCEntNetPPP); 3670 if (!isA_CFDictionary(pppConfig)) { 3671 continue; 3672 } 3673 3674 autodial = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandEnabled); 3675 if (!isA_CFNumber(autodial)) { 3676 continue; 3677 } 3678 3679 CFNumberGetValue(autodial, kCFNumberIntType, &val); 3680 if (val) { 3681 CFArrayRef domains; 3682 CFIndex domainsCount; 3683 CFIndex domainsIndex; 3684 3685 /* we found an conditional connection enabled configuration */ 3686 3687 /* check domain */ 3688 domains = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandDomains); 3689 if (!isA_CFArray(domains)) { 3690 continue; 3691 } 3692 3693 domainsCount = CFArrayGetCount(domains); 3694 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) { 3695 CFStringRef domain; 3696 3697 domain = CFArrayGetValueAtIndex(domains, domainsIndex); 3698 if (!isA_CFString(domain)) { 3699 continue; 3700 } 3701 3702 if (!catchAllFound && 3703 (CFStringCompare(domain, CFSTR(""), 0) == kCFCompareEqualTo 3704 || CFStringCompare(domain, CFSTR("."), 0) == kCFCompareEqualTo)) 3705 { 3706 // found a catch all 3707 catchAllFound = TRUE; 3708 catchAllService = serviceIndex; 3709 catchAllConfig = configIndex; 3710 } 3711 3712 if (_SC_domainEndsWithDomain(hostName, domain)) { 3713 // found matching configuration 3714 *serviceID = serviceName; 3715 CFRetain(*serviceID); 3716 *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config); 3717 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName); 3718 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority); 3719 addPasswordFromKeychain(*serviceID, userOptions); 3720 success = TRUE; 3721 goto done_selection; 3722 } 3723 } 3724 } 3725 } 3726 } 3727 3728 // config not found, do we have a catchall ? 3729 if (catchAllFound) { 3730 CFDictionaryRef config; 3731 CFArrayRef serviceConfigs; 3732 CFStringRef serviceName; 3733 3734 serviceName = CFArrayGetValueAtIndex(serviceNames, catchAllService); 3735 serviceConfigs = CFDictionaryGetValue(services, serviceName); 3736 config = CFArrayGetValueAtIndex(serviceConfigs, catchAllConfig); 3737 3738 *serviceID = serviceName; 3739 CFRetain(*serviceID); 3740 *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config); 3741 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName); 3742 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority); 3743 addPasswordFromKeychain(*serviceID, userOptions); 3744 success = TRUE; 3745 goto done_selection; 3746 } 3747 3748 done_selection: 3749 3750 if (serviceNames) { 3751 CFRelease(serviceNames); 3752 } 3753 if (services) { 3754 CFRelease(services); 3755 } 3756 3757 if (debug > 1) { 3758 SC_log(LOG_INFO, "SCNetworkConnectionCopyUserPreferences %s", success ? "succeeded" : "failed"); 3759 SC_log(LOG_INFO, "Selection options: %@", selectionOptions); 3760 } 3761 3762 return success; 3763 } 3764 3765 /* we don't have selection options */ 3766 3767 // (1) Figure out which service ID we care about, allocate it into passed "serviceID" 3768 success = SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(serviceID); 3769 3770 if (success && (*serviceID != NULL)) { 3771 // (2) Get the list of user data for this service ID 3772 CFPropertyListRef userServices = NULL; 3773 3774 3775 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't 3776 if (userServices != NULL) { 3777 if (isA_CFArray(userServices)) { 3778 // (4) Get the default set of user options for this service 3779 success = SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef)userServices, 3780 userOptions); 3781 if (success) { 3782 addPasswordFromKeychain(*serviceID, userOptions); 3783 } 3784 } else { 3785 SC_log(LOG_INFO, "Error, userServices are not of type CFArray!"); 3786 } 3787 3788 CFRelease(userServices); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL 3789 } 3790 } 3791 3792 if (debug > 1) { 3793 SC_log(LOG_INFO, "SCNetworkConnectionCopyUserPreferences %@, no selection options", 3794 success ? CFSTR("succeeded") : CFSTR("failed")); 3795 } 3796 3797 return success; 3798} 3799 3800 3801Boolean 3802SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions, 3803 CFStringRef *serviceID, 3804 CFDictionaryRef *userOptions) 3805{ 3806 Boolean success = FALSE; 3807 3808 /* initialize runtime */ 3809 pthread_once(&initialized, __SCNetworkConnectionInitialize); 3810 3811 /* first check for new VPN OnDemand style */ 3812 if (selectionOptions != NULL) { 3813 CFStringRef hostName; 3814 3815 hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName); 3816 if (isA_CFString(hostName)) { 3817 CFStringRef connectionServiceID = NULL; 3818 SCNetworkConnectionStatus connectionStatus = kSCNetworkConnectionInvalid; 3819 Boolean onDemandRetry; 3820 CFTypeRef val; 3821 3822 val = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry); 3823 onDemandRetry = isA_CFBoolean(val) ? CFBooleanGetValue(val) : TRUE; 3824 3825 success = __SCNetworkConnectionCopyOnDemandInfoWithName(NULL, 3826 hostName, 3827 onDemandRetry, 3828 &connectionServiceID, 3829 &connectionStatus, 3830 NULL); 3831 if (debug > 1) { 3832 SC_log(LOG_INFO, "__SCNetworkConnectionCopyOnDemandInfoWithName: return %d, status %d", 3833 success, 3834 connectionStatus); 3835 } 3836 3837 if (success) { 3838 // if the hostname matches an OnDemand domain 3839 if (connectionStatus == kSCNetworkConnectionConnected) { 3840 // if we are already connected 3841 if (connectionServiceID != NULL) { 3842 CFRelease(connectionServiceID); 3843 } 3844 return FALSE; 3845 } 3846 3847 *serviceID = connectionServiceID; 3848 *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 3849 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName); 3850 return TRUE; 3851 } else if (!onDemandRetry) { 3852 // if the hostname does not match an OnDemand domain and we have 3853 // not yet issued an initial DNS query (i.e. it's not a query 3854 // being retried after the VPN has been established) then we're 3855 // done 3856 return FALSE; 3857 } 3858 } 3859 } 3860 3861 return __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions, serviceID, userOptions); 3862} 3863 3864 3865Boolean 3866SCNetworkConnectionOnDemandShouldRetryOnFailure (SCNetworkConnectionRef connection) 3867{ 3868 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 3869 CFDictionaryRef match_info = NULL; 3870 3871 if (!isA_SCNetworkConnection(connection)) { 3872 _SCErrorSet(kSCStatusInvalidArgument); 3873 goto fail; 3874 } 3875 3876 if (connectionPrivate->service == NULL) { 3877 _SCErrorSet(kSCStatusConnectionNoService); 3878 goto fail; 3879 } 3880 3881 if (isA_CFDictionary(connectionPrivate->on_demand_user_options)) { 3882 match_info = CFDictionaryGetValue(connectionPrivate->on_demand_user_options, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo); 3883 if (isA_CFDictionary(match_info)) { 3884 CFBooleanRef onRetry = CFDictionaryGetValue(match_info, kSCNetworkConnectionOnDemandMatchInfoOnRetry); 3885 if (isA_CFBoolean(onRetry)) { 3886 return CFBooleanGetValue(onRetry); 3887 } 3888 } 3889 } 3890 3891 fail: 3892 return FALSE; 3893} 3894 3895 3896// Mask is optional in routes dictionary; if not present, whole addresses are matched 3897static Boolean 3898__SCNetworkConnectionIPv4AddressMatchesRoutes (struct sockaddr_in *addr_in, CFDictionaryRef routes) 3899{ 3900 CFIndex count; 3901 CFIndex i; 3902 CFDataRef maskData = NULL; 3903 struct in_addr *maskDataArray; 3904 CFDataRef routeaddrData = NULL; 3905 struct in_addr *routeaddrDataArray; 3906 3907 if (!isA_CFDictionary(routes)) { 3908 return FALSE; 3909 } 3910 3911 routeaddrData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoAddresses); 3912 maskData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoMasks); 3913 3914 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */ 3915 if (!isA_CFData(routeaddrData) || (maskData && (!isA_CFData(maskData) || CFDataGetLength(routeaddrData) != CFDataGetLength(maskData)))) { 3916 return FALSE; 3917 } 3918 3919 routeaddrDataArray = (struct in_addr*)(void*)CFDataGetBytePtr(routeaddrData); 3920 if (maskData) { 3921 maskDataArray = (struct in_addr*)(void*)CFDataGetBytePtr(maskData); 3922 } 3923 3924 count = CFDataGetLength(routeaddrData) / sizeof(struct in_addr); 3925 for (i=0; i<count; i++) { 3926 struct in_addr routeAddr = *routeaddrDataArray; 3927 3928 if (maskData) { 3929 struct in_addr mask = *maskDataArray; 3930 3931 if ((addr_in->sin_addr.s_addr & mask.s_addr) == (routeAddr.s_addr & mask.s_addr)) { 3932 return TRUE; 3933 } 3934 maskDataArray++; 3935 } else { 3936 if (addr_in->sin_addr.s_addr == routeAddr.s_addr) { 3937 return TRUE; 3938 } 3939 } 3940 routeaddrDataArray++; 3941 } 3942 return FALSE; 3943} 3944 3945 3946static void 3947__SCNetworkConnectionMaskIPv6Address(struct in6_addr *addr, struct in6_addr *mask) 3948{ 3949 for (size_t i = 0; i < sizeof(struct in6_addr); i++) 3950 addr->s6_addr[i] &= mask->s6_addr[i]; 3951} 3952 3953 3954// Mask is optional in routes dictionary; if not present, whole addresses are matched 3955static Boolean 3956__SCNetworkConnectionIPv6AddressMatchesRoutes (struct sockaddr_in6 *addr_in6, CFDictionaryRef routes) 3957{ 3958 CFIndex count; 3959 CFIndex i; 3960 CFDataRef maskData = NULL; 3961 struct in6_addr *maskDataArray; 3962 CFDataRef routeaddrData = NULL; 3963 struct in6_addr *routeaddrDataArray; 3964 3965 if (!isA_CFDictionary(routes)) { 3966 return FALSE; 3967 } 3968 3969 routeaddrData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoAddresses); 3970 maskData = CFDictionaryGetValue(routes, kSCNetworkConnectionNetworkInfoMasks); 3971 3972 /* routeaddrData and maskData are packed arrays of addresses; make sure they have the same length */ 3973 if (!isA_CFData(routeaddrData) || (maskData && (!isA_CFData(maskData) || CFDataGetLength(routeaddrData) != CFDataGetLength(maskData)))) { 3974 return FALSE; 3975 } 3976 3977 routeaddrDataArray = (struct in6_addr*)(void*)CFDataGetBytePtr(routeaddrData); 3978 if (maskData) { 3979 maskDataArray = (struct in6_addr*)(void*)CFDataGetBytePtr(maskData); 3980 } 3981 3982 count = CFDataGetLength(routeaddrData) / sizeof(struct in6_addr); 3983 for (i=0; i<count; i++) { 3984 if (maskData) { 3985 struct in6_addr cmpAddr; 3986 struct in6_addr *mask = maskDataArray; 3987 struct in6_addr routeAddr; 3988 3989 memcpy(&routeAddr, routeaddrDataArray, sizeof(routeAddr)); 3990 memcpy(&cmpAddr, &addr_in6->sin6_addr, sizeof(cmpAddr)); 3991 __SCNetworkConnectionMaskIPv6Address(&routeAddr, mask); 3992 __SCNetworkConnectionMaskIPv6Address(&cmpAddr, mask); 3993 maskDataArray++; 3994 if (!memcmp(&routeAddr, &cmpAddr, sizeof(routeAddr))) { 3995 return TRUE; 3996 } 3997 } else { 3998 if (!memcmp(routeaddrDataArray, &addr_in6->sin6_addr, sizeof(struct in6_addr))) { 3999 return TRUE; 4000 } 4001 } 4002 4003 routeaddrDataArray++; 4004 } 4005 return FALSE; 4006} 4007 4008 4009static Boolean 4010__SCNetworkConnectionAddressMatchesRedirectedDNS(CFDictionaryRef trigger, const struct sockaddr *input_addr) 4011{ 4012 CFBooleanRef redirectedRef = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandDNSRedirectDetected); 4013 4014 if (isA_CFBoolean(redirectedRef) && CFBooleanGetValue(redirectedRef)) { 4015 /* DNS is redirected. Look for address list. */ 4016 CFDictionaryRef redirectedAddressesRef = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandDNSRedirectedAddresses); 4017 4018 if (isA_CFDictionary(redirectedAddressesRef)) { 4019 if (input_addr->sa_family == AF_INET) { 4020 return __SCNetworkConnectionIPv4AddressMatchesRoutes((struct sockaddr_in*)(void*)input_addr, CFDictionaryGetValue(redirectedAddressesRef, kSCNetworkConnectionNetworkInfoIPv4)); 4021 } else if (input_addr->sa_family == AF_INET6) { 4022 return __SCNetworkConnectionIPv6AddressMatchesRoutes((struct sockaddr_in6*)(void*)input_addr, CFDictionaryGetValue(redirectedAddressesRef, kSCNetworkConnectionNetworkInfoIPv6)); 4023 } 4024 } 4025 } 4026 4027 return FALSE; 4028} 4029 4030/* If the required probe has failed, we need to tunnel the address. Equivalent to redirected DNS. */ 4031static Boolean 4032__SCNetworkConnectionRequiredProbeFailed (CFDictionaryRef trigger, CFStringRef probeString) 4033{ 4034 CFDictionaryRef probeResults = NULL; 4035 4036 if (!isA_CFString(probeString)) { 4037 return FALSE; 4038 } 4039 4040 probeResults = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandProbeResults); 4041 if (!isA_CFDictionary(probeResults)) { 4042 return TRUE; 4043 } 4044 4045 CFBooleanRef result = CFDictionaryGetValue(probeResults, probeString); 4046 4047 /* Only a value of kCFBooleanFalse marks the probe as failed */ 4048 return (isA_CFBoolean(result) && !CFBooleanGetValue(result)); 4049} 4050 4051Boolean 4052SCNetworkConnectionCanTunnelAddress (SCNetworkConnectionRef connection, const struct sockaddr *address, Boolean *startImmediately) 4053{ 4054 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 4055 CFStringRef serviceID = NULL; 4056 CFDictionaryRef configuration = NULL; 4057 CFDictionaryRef trigger = NULL; 4058 CFDictionaryRef tunneledNetworks = NULL; 4059 sa_family_t address_family = AF_UNSPEC; 4060 Boolean success = FALSE; 4061 4062 if (startImmediately) { 4063 *startImmediately = FALSE; 4064 } 4065 4066 if (address == NULL) { 4067 goto done; 4068 } 4069 4070 address_family = address->sa_family; 4071 if (address_family != AF_INET && address_family != AF_INET6) { 4072 goto done; 4073 } 4074 4075 if (!isA_SCNetworkConnection(connection)) { 4076 _SCErrorSet(kSCStatusInvalidArgument); 4077 goto done; 4078 } 4079 4080 if (connectionPrivate->service == NULL) { 4081 _SCErrorSet(kSCStatusConnectionNoService); 4082 goto done; 4083 } 4084 4085 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service); 4086 if (!isA_CFString(serviceID)) { 4087 goto done; 4088 } 4089 4090 configuration = __SCNetworkConnectionCopyOnDemandConfiguration(); 4091 if (configuration == NULL) { 4092 goto done; 4093 } 4094 4095 trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, serviceID); 4096 if (trigger == NULL) { 4097 goto done; 4098 } 4099 4100 if (__SCNetworkConnectionRequiredProbeFailed(trigger, connectionPrivate->on_demand_required_probe)) { 4101 /* If probe failed, we can't trust DNS - connect now */ 4102 if (startImmediately) { 4103 *startImmediately = TRUE; 4104 } 4105 success = TRUE; 4106 goto done; 4107 } 4108 4109 if (__SCNetworkConnectionAddressMatchesRedirectedDNS(trigger, address)) { 4110 if (startImmediately) { 4111 *startImmediately = TRUE; 4112 } 4113 success = TRUE; 4114 goto done; 4115 } 4116 4117 tunneledNetworks = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandTunneledNetworks); 4118 if (!isA_CFDictionary(tunneledNetworks)) { 4119 goto done; 4120 } 4121 4122 if (address_family == AF_INET) { 4123 CFDictionaryRef ip_dict; 4124 Boolean matches = FALSE; 4125 struct sockaddr_in *addr_in = (struct sockaddr_in *)(void*)address; 4126 4127 ip_dict = CFDictionaryGetValue(tunneledNetworks, kSCNetworkConnectionNetworkInfoIPv4); 4128 if (!isA_CFDictionary(ip_dict)) { 4129 goto done; 4130 } 4131 4132 matches = __SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in, CFDictionaryGetValue(ip_dict, kSCNetworkConnectionNetworkInfoIncludedRoutes)); 4133 4134 if (matches) { 4135 if (!__SCNetworkConnectionIPv4AddressMatchesRoutes(addr_in, CFDictionaryGetValue(ip_dict, kSCNetworkConnectionNetworkInfoExcludedRoutes))) { 4136 success = TRUE; 4137 goto done; 4138 } 4139 } 4140 } else { 4141 CFDictionaryRef ip6_dict; 4142 Boolean matches = FALSE; 4143 struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)(void*)address; 4144 4145 ip6_dict = CFDictionaryGetValue(tunneledNetworks, kSCNetworkConnectionNetworkInfoIPv6); 4146 if (!isA_CFDictionary(ip6_dict)) { 4147 goto done; 4148 } 4149 4150 matches = __SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6, CFDictionaryGetValue(ip6_dict, kSCNetworkConnectionNetworkInfoIncludedRoutes)); 4151 4152 if (matches) { 4153 if (!__SCNetworkConnectionIPv6AddressMatchesRoutes(addr_in6, CFDictionaryGetValue(ip6_dict, kSCNetworkConnectionNetworkInfoExcludedRoutes))) { 4154 success = TRUE; 4155 goto done; 4156 } 4157 } 4158 } 4159done: 4160 if (configuration) { 4161 CFRelease(configuration); 4162 } 4163 if (trigger) { 4164 CFRelease(trigger); 4165 } 4166 return success; 4167} 4168 4169Boolean 4170SCNetworkConnectionSelectServiceWithOptions(SCNetworkConnectionRef connection, CFDictionaryRef selectionOptions) 4171{ 4172 CFStringRef account_identifier = NULL; 4173 CFDictionaryRef configuration = NULL; 4174 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection; 4175 CFDictionaryRef found_trigger = NULL; 4176 CFStringRef host_name = NULL; 4177 Boolean is_retry = TRUE; 4178 CFDictionaryRef match_info = NULL; 4179 CFMutableDictionaryRef new_user_options = NULL; 4180 SCNetworkConnectionStatus on_demand_status = kSCNetworkConnectionInvalid; 4181 CFStringRef requiredProbe = NULL; 4182 CFStringRef service_id = NULL; 4183 Boolean skip_prefs = FALSE; 4184 Boolean success = TRUE; 4185 CFDictionaryRef user_options = NULL; 4186 4187 if (!isA_SCNetworkConnection(connection)) { 4188 _SCErrorSet(kSCStatusInvalidArgument); 4189 success = FALSE; 4190 goto done; 4191 } 4192 4193 /* Can't call this on a connection that is already associated with a service */ 4194 if (connectionPrivate->service != NULL) { 4195 _SCErrorSet(kSCStatusInvalidArgument); 4196 success = FALSE; 4197 goto done; 4198 } 4199 4200 if (isA_CFDictionary(selectionOptions)) { 4201 CFBooleanRef no_user_prefs = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionNoUserPrefs); 4202 CFBooleanRef retry = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry); 4203 4204 account_identifier = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandAccountIdentifier); 4205 host_name = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName); 4206 skip_prefs = (isA_CFBoolean(no_user_prefs) && CFBooleanGetValue(no_user_prefs)); 4207 4208 if (isA_CFBoolean(retry)) { 4209 is_retry = CFBooleanGetValue(retry); 4210 } 4211 } 4212 4213 configuration = __SCNetworkConnectionCopyOnDemandConfiguration(); 4214 4215 /* First, check for a match with the App Layer rules */ 4216 service_id = VPNAppLayerCopyMatchingService(connectionPrivate->client_audit_token, 4217 connectionPrivate->client_pid, 4218 connectionPrivate->client_uuid, 4219 connectionPrivate->client_bundle_id, 4220 host_name, 4221 account_identifier, 4222 &match_info); 4223 if (service_id != NULL) { 4224 Boolean use_app_layer = TRUE; 4225 4226 if (isA_CFDictionary(configuration)) { 4227 found_trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, service_id); 4228 if (found_trigger != NULL) { 4229 CFNumberRef status_num; 4230 4231 if (!CFDictionaryGetValueIfPresent(found_trigger, 4232 kSCNetworkConnectionOnDemandStatus, 4233 (const void **) &status_num) || 4234 !isA_CFNumber(status_num) || 4235 !CFNumberGetValue(status_num, kCFNumberSInt32Type, &on_demand_status)) { 4236 on_demand_status = kSCNetworkConnectionInvalid; 4237 } 4238 4239 /* 4240 * If the trigger should be ignored, still use App Layer VPN if it is already connected or 4241 * is in the process of connecting. 4242 */ 4243 if (__SCNetworkConnectionShouldIgnoreTrigger(found_trigger) && 4244 (on_demand_status != kSCNetworkConnectionConnecting) && 4245 (on_demand_status != kSCNetworkConnectionConnected)) { 4246 use_app_layer = FALSE; 4247 } 4248 } 4249 } 4250 4251 if (use_app_layer) { 4252 /* If this is not the 'OnRetry' call, and the service has not yet started, the match may need to return false */ 4253 if (!is_retry && 4254 match_info != NULL && 4255 on_demand_status != kSCNetworkConnectionConnecting && 4256 on_demand_status != kSCNetworkConnectionConnected) { 4257 CFBooleanRef matchedOnRetry = CFDictionaryGetValue(match_info, kSCNetworkConnectionOnDemandMatchInfoOnRetry); 4258 if (matchedOnRetry && CFBooleanGetValue(matchedOnRetry)) { 4259 /* Don't return that we matched always; wait for SCNetworkConnectionOnDemandShouldRetryOnFailure */ 4260 success = FALSE; 4261 } 4262 } 4263 connectionPrivate->type = kSCNetworkConnectionTypeAppLayerVPN; 4264 goto search_done; 4265 } else { 4266 CFRelease(service_id); 4267 service_id = NULL; 4268 if (match_info != NULL) { 4269 CFRelease(match_info); 4270 match_info = NULL; 4271 } 4272 if (found_trigger != NULL) { 4273 CFRelease(found_trigger); 4274 found_trigger = NULL; 4275 } 4276 } 4277 } 4278 4279 /* Next, check the IP layer rules */ 4280 if (isA_CFDictionary(configuration) && host_name != NULL) { 4281 Boolean triggerNow = FALSE; 4282 4283 found_trigger = __SCNetworkConnectionCopyMatchingTriggerWithName(configuration, host_name, connectionPrivate->client_pid, is_retry, &match_info, &triggerNow, &requiredProbe); 4284 if (found_trigger != NULL) { 4285 service_id = CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandServiceID); 4286 if (isA_CFString(service_id)) { 4287 CFRetain(service_id); 4288 connectionPrivate->type = kSCNetworkConnectionTypeIPLayerVPN; 4289 } else { 4290 service_id = NULL; 4291 } 4292 if (!triggerNow) { 4293 success = FALSE; 4294 } 4295 goto search_done; 4296 } else if (!is_retry) { 4297 goto search_done; 4298 } 4299 4300 if (match_info != NULL) { 4301 CFRelease(match_info); 4302 match_info = NULL; 4303 } 4304 } 4305 4306 /* Next, check the user preferences */ 4307 if (!skip_prefs && __SCNetworkConnectionCopyUserPreferencesInternal(selectionOptions, &service_id, &user_options)) { 4308 CFMutableDictionaryRef minfo; 4309 CFNumberRef type_num; 4310 4311 if (isA_CFDictionary(configuration)) { 4312 found_trigger = __SCNetworkConnectionCopyTriggerWithService(configuration, service_id); 4313 } 4314 connectionPrivate->type = kSCNetworkConnectionTypePPP; 4315 4316 minfo = CFDictionaryCreateMutable(NULL, 4317 0, 4318 &kCFTypeDictionaryKeyCallBacks, 4319 &kCFTypeDictionaryValueCallBacks); 4320 type_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &connectionPrivate->type); 4321 CFDictionarySetValue(minfo, kSCNetworkConnectionOnDemandMatchInfoVPNType, type_num); 4322 CFRelease(type_num); 4323 match_info = minfo; 4324 goto search_done; 4325 } 4326 4327 search_done: 4328 if (service_id == NULL) { 4329 _SCErrorSet(kSCStatusOK); 4330 success = FALSE; 4331 goto done; 4332 } 4333 4334 connectionPrivate->service = _SCNetworkServiceCopyActive(NULL, service_id); 4335 if (connectionPrivate->service == NULL) { 4336 _SCErrorSet(kSCStatusOK); 4337 success = FALSE; 4338 goto done; 4339 } 4340 4341 if (found_trigger != NULL) { 4342 if (connectionPrivate->on_demand_info) { 4343 CFRelease(connectionPrivate->on_demand_info); 4344 } 4345 connectionPrivate->on_demand_info = found_trigger; 4346 CFRetain(connectionPrivate->on_demand_info); 4347 4348 if (on_demand_status == kSCNetworkConnectionInvalid) { 4349 CFNumberRef status_num; 4350 4351 if (!CFDictionaryGetValueIfPresent(found_trigger, 4352 kSCNetworkConnectionOnDemandStatus, 4353 (const void **) &status_num) || 4354 !isA_CFNumber(status_num) || 4355 !CFNumberGetValue(status_num, kCFNumberSInt32Type, &on_demand_status)) { 4356 on_demand_status = kSCNetworkConnectionInvalid; 4357 } 4358 } 4359 4360 if (on_demand_status != kSCNetworkConnectionConnected) { 4361 if (connectionPrivate->type == kSCNetworkConnectionTypeAppLayerVPN) { 4362 /* Check App Layer OnDemand flag */ 4363 CFBooleanRef app_on_demand_enabled = 4364 CFDictionaryGetValue(found_trigger, kSCNetworkConnectionOnDemandMatchAppEnabled); 4365 if (isA_CFBoolean(app_on_demand_enabled) && CFBooleanGetValue(app_on_demand_enabled)) { 4366 connectionPrivate->on_demand = TRUE; 4367 } 4368 } else { 4369 connectionPrivate->on_demand = TRUE; 4370 } 4371 } 4372 } else if (connectionPrivate->type == kSCNetworkConnectionTypePPP) { 4373 /* If we got the service from __SCNetworkConnectionCopyUserPreferencesInternal, then it's on demand */ 4374 connectionPrivate->on_demand = TRUE; 4375 } 4376 4377 if (user_options == NULL) { 4378 new_user_options = CFDictionaryCreateMutable(kCFAllocatorDefault, 4379 0, 4380 &kCFTypeDictionaryKeyCallBacks, 4381 &kCFTypeDictionaryValueCallBacks); 4382 } else { 4383 new_user_options = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, user_options); 4384 } 4385 4386 if (host_name != NULL) { 4387 CFDictionarySetValue(new_user_options, kSCNetworkConnectionSelectionOptionOnDemandHostName, host_name); 4388 } 4389 4390 if (connectionPrivate->on_demand && match_info != NULL) { 4391 CFDictionarySetValue(new_user_options, kSCNetworkConnectionSelectionOptionOnDemandMatchInfo, match_info); 4392 } 4393 4394 connectionPrivate->on_demand_user_options = new_user_options; 4395 CFRetain(connectionPrivate->on_demand_user_options); 4396 4397 if (requiredProbe) { 4398 connectionPrivate->on_demand_required_probe = requiredProbe; 4399 CFRetain(connectionPrivate->on_demand_required_probe); 4400 } 4401 4402done: 4403 if (service_id != NULL) { 4404 CFRelease(service_id); 4405 } 4406 4407 if (configuration != NULL) { 4408 CFRelease(configuration); 4409 } 4410 4411 if (found_trigger != NULL) { 4412 CFRelease(found_trigger); 4413 } 4414 4415 if (user_options != NULL) { 4416 CFRelease(user_options); 4417 } 4418 4419 if (new_user_options != NULL) { 4420 CFRelease(new_user_options); 4421 } 4422 4423 if (match_info != NULL) { 4424 CFRelease(match_info); 4425 } 4426 4427 if (requiredProbe != NULL) { 4428 CFRelease(requiredProbe); 4429 } 4430 4431 return success; 4432} 4433 4434//******************************************************************************************* 4435// SCNetworkConnectionPrivateCopyDefaultServiceIDForDial 4436// ---------------------------------------------------- 4437// Try to find the service id to connect 4438// (1) Start by looking at the last service used in Network Pref / Network menu extra 4439// (2) If Network Pref / Network menu extra has not been used, find the PPP service 4440// with the highest ordering 4441//******************************************************************************************** 4442static Boolean 4443SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(CFStringRef *serviceID) 4444{ 4445 Boolean foundService = FALSE; 4446 CFPropertyListRef lastServiceSelectedInIC = NULL; 4447 4448 4449 // we found the service the user last had open in IC 4450 if (lastServiceSelectedInIC != NULL) { 4451 // make sure its a PPP service 4452 if (SCNetworkConnectionPrivateIsPPPService(lastServiceSelectedInIC, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) { 4453 // make sure the service that we found is valid 4454 CFDictionaryRef dict; 4455 CFStringRef key; 4456 4457 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 4458 kSCDynamicStoreDomainSetup, 4459 lastServiceSelectedInIC, 4460 kSCEntNetInterface); 4461 dict = SCDynamicStoreCopyValue(NULL, key); 4462 CFRelease(key); 4463 if (dict != NULL) { 4464 CFRelease(dict); 4465 *serviceID = CFRetain(lastServiceSelectedInIC); 4466 foundService = TRUE; 4467 } 4468 } 4469 CFRelease(lastServiceSelectedInIC); 4470 } 4471 4472 if (!foundService) { 4473 foundService = SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(serviceID); 4474 } 4475 4476 return foundService; 4477} 4478 4479//******************************************************************************** 4480// SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore 4481// ------------------------------------------------------- 4482// Find the highest ordered PPP service in the dynamic store 4483//******************************************************************************** 4484static Boolean 4485SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(CFStringRef *serviceID) 4486{ 4487 CFDictionaryRef dict = NULL; 4488 CFStringRef key = NULL; 4489 CFArrayRef serviceIDs = NULL; 4490 Boolean success = FALSE; 4491 4492 *serviceID = NULL; 4493 4494 do { 4495 CFIndex count; 4496 CFIndex i; 4497 4498 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainSetup, kSCEntNetIPv4); 4499 if (key == NULL) { 4500 fprintf(stderr, "Error, Setup Key == NULL!\n"); 4501 break; 4502 } 4503 4504 dict = SCDynamicStoreCopyValue(NULL, key); 4505 if (!isA_CFDictionary(dict)) { 4506 fprintf(stderr, "no global IPv4 entity\n"); 4507 break; 4508 } 4509 4510 serviceIDs = CFDictionaryGetValue(dict, kSCPropNetServiceOrder); // array of service id's 4511 if (!isA_CFArray(serviceIDs)) { 4512 fprintf(stderr, "service order not specified\n"); 4513 break; 4514 } 4515 4516 count = CFArrayGetCount(serviceIDs); 4517 for (i = 0; i < count; i++) { 4518 CFStringRef service = CFArrayGetValueAtIndex(serviceIDs, i); 4519 4520 if (SCNetworkConnectionPrivateIsPPPService(service, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) { 4521 *serviceID = CFRetain(service); 4522 success = TRUE; 4523 break; 4524 } 4525 } 4526 } while (FALSE); 4527 4528 if (key != NULL) CFRelease(key); 4529 if (dict != NULL) CFRelease(dict); 4530 4531 return success; 4532} 4533 4534//******************************************************************************** 4535// SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray 4536// --------------------------------------------------------- 4537// Copy over user preferences for a particular service if they exist 4538//******************************************************************************** 4539static Boolean 4540SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions) 4541{ 4542 CFIndex count = CFArrayGetCount(userOptionsArray); 4543 int i; 4544 4545 for (i = 0; i < count; i++) { 4546 // (1) Find the dictionary 4547 CFPropertyListRef propertyList = CFArrayGetValueAtIndex(userOptionsArray, i); 4548 4549 if (isA_CFDictionary(propertyList) != NULL) { 4550 // See if there's a value for dial on demand 4551 CFPropertyListRef value; 4552 4553 value = CFDictionaryGetValue((CFDictionaryRef)propertyList, k_Dial_Default_Key); 4554 if (isA_CFBoolean(value) != NULL) { 4555 if (CFBooleanGetValue(value)) { 4556 // we found the default user options 4557 *userOptions = CFDictionaryCreateCopy(NULL, 4558 (CFDictionaryRef)propertyList); 4559 break; 4560 } 4561 } 4562 } 4563 } 4564 4565 return TRUE; 4566} 4567 4568//******************************************************************************** 4569// SCNetworkConnectionPrivateIsServiceType 4570// -------------------------------------- 4571// Check and see if the service is a PPP service of the given types 4572//******************************************************************************** 4573static Boolean 4574SCNetworkConnectionPrivateIsPPPService(CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2) 4575{ 4576 CFStringRef entityKey; 4577 Boolean isPPPService = FALSE; 4578 Boolean isMatchingSubType = FALSE; 4579 CFDictionaryRef serviceDict; 4580 4581 entityKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 4582 kSCDynamicStoreDomainSetup, 4583 serviceID, 4584 kSCEntNetInterface); 4585 if (entityKey == NULL) { 4586 return FALSE; 4587 } 4588 4589 serviceDict = SCDynamicStoreCopyValue(NULL, entityKey); 4590 if (serviceDict != NULL) { 4591 if (isA_CFDictionary(serviceDict)) { 4592 CFStringRef type; 4593 CFStringRef subtype; 4594 4595 type = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceType); 4596 if (isA_CFString(type)) { 4597 isPPPService = CFEqual(type, kSCValNetInterfaceTypePPP); 4598 } 4599 4600 subtype = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceSubType); 4601 if (isA_CFString(subtype)) { 4602 isMatchingSubType = CFEqual(subtype, subType1); 4603 if (!isMatchingSubType && subType2) 4604 isMatchingSubType = CFEqual(subtype, subType2); 4605 } 4606 } 4607 CFRelease(serviceDict); 4608 } 4609 CFRelease(entityKey); 4610 4611 return (isPPPService && isMatchingSubType); 4612} 4613 4614//******************************************************************************** 4615// addPasswordFromKeychain 4616// -------------------------------------- 4617// Get the password and shared secret out of the keychain and add 4618// them to the PPP and IPSec dictionaries 4619//******************************************************************************** 4620static void 4621addPasswordFromKeychain(CFStringRef serviceID, CFDictionaryRef *userOptions) 4622{ 4623 CFPropertyListRef uniqueID; 4624 CFStringRef password; 4625 CFStringRef sharedsecret = NULL; 4626 4627 /* user options must exist */ 4628 if (*userOptions == NULL) 4629 return; 4630 4631 /* first, get the unique identifier used to store passwords in the keychain */ 4632 uniqueID = CFDictionaryGetValue(*userOptions, k_Unique_Id_Key); 4633 if (!isA_CFString(uniqueID)) 4634 return; 4635 4636 /* first, get the PPP password */ 4637 password = copyPasswordFromKeychain(uniqueID); 4638 4639 /* then, if necessary, get the IPSec Shared Secret */ 4640 if (SCNetworkConnectionPrivateIsPPPService(serviceID, kSCValNetInterfaceSubTypeL2TP, 0)) { 4641 CFMutableStringRef uniqueIDSS; 4642 4643 uniqueIDSS = CFStringCreateMutableCopy(NULL, 0, uniqueID); 4644 CFStringAppend(uniqueIDSS, CFSTR(".SS")); 4645 sharedsecret = copyPasswordFromKeychain(uniqueIDSS); 4646 CFRelease(uniqueIDSS); 4647 } 4648 4649 /* did we find our information in the key chain ? */ 4650 if ((password != NULL) || (sharedsecret != NULL)) { 4651 CFMutableDictionaryRef newOptions; 4652 4653 newOptions = CFDictionaryCreateMutableCopy(NULL, 0, *userOptions); 4654 4655 /* PPP password */ 4656 if (password != NULL) { 4657 CFDictionaryRef entity; 4658 CFMutableDictionaryRef newEntity; 4659 4660 entity = CFDictionaryGetValue(*userOptions, kSCEntNetPPP); 4661 if (isA_CFDictionary(entity)) 4662 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity); 4663 else 4664 newEntity = CFDictionaryCreateMutable(NULL, 4665 0, 4666 &kCFTypeDictionaryKeyCallBacks, 4667 &kCFTypeDictionaryValueCallBacks); 4668 4669 4670 /* set the PPP password */ 4671 CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPassword, uniqueID); 4672 CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPasswordEncryption, kSCValNetPPPAuthPasswordEncryptionKeychain); 4673 CFRelease(password); 4674 4675 /* update the PPP entity */ 4676 CFDictionarySetValue(newOptions, kSCEntNetPPP, newEntity); 4677 CFRelease(newEntity); 4678 } 4679 4680 /* IPSec Shared Secret */ 4681 if (sharedsecret != NULL) { 4682 CFDictionaryRef entity; 4683 CFMutableDictionaryRef newEntity; 4684 4685 entity = CFDictionaryGetValue(*userOptions, kSCEntNetIPSec); 4686 if (isA_CFDictionary(entity)) 4687 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity); 4688 else 4689 newEntity = CFDictionaryCreateMutable(NULL, 4690 0, 4691 &kCFTypeDictionaryKeyCallBacks, 4692 &kCFTypeDictionaryValueCallBacks); 4693 4694 /* set the IPSec Shared Secret */ 4695 CFDictionarySetValue(newEntity, kSCPropNetIPSecSharedSecret, sharedsecret); 4696 CFRelease(sharedsecret); 4697 4698 /* update the IPSec entity */ 4699 CFDictionarySetValue(newOptions, kSCEntNetIPSec, newEntity); 4700 CFRelease(newEntity); 4701 } 4702 4703 /* update the userOptions dictionary */ 4704 CFRelease(*userOptions); 4705 *userOptions = CFDictionaryCreateCopy(NULL, newOptions); 4706 CFRelease(newOptions); 4707 } 4708 4709} 4710 4711#if !TARGET_OS_IPHONE 4712//******************************************************************************** 4713// copyKeychainEnumerator 4714// -------------------------------------- 4715// Gather Keychain Enumerator 4716//******************************************************************************** 4717static CFArrayRef 4718copyKeychainEnumerator(CFStringRef uniqueIdentifier) 4719{ 4720 CFArrayRef itemArray = NULL; 4721 CFMutableDictionaryRef query; 4722 OSStatus result; 4723 4724 query = CFDictionaryCreateMutable(NULL, 4725 0, 4726 &kCFTypeDictionaryKeyCallBacks, 4727 &kCFTypeDictionaryValueCallBacks); 4728 CFDictionarySetValue(query, kSecClass , kSecClassGenericPassword); 4729 CFDictionarySetValue(query, kSecAttrService, uniqueIdentifier); 4730 CFDictionarySetValue(query, kSecReturnRef , kCFBooleanTrue); 4731 CFDictionarySetValue(query, kSecMatchLimit , kSecMatchLimitAll); 4732 result = SecItemCopyMatching(query, (CFTypeRef *)&itemArray); 4733 CFRelease(query); 4734 if ((result != noErr) && (itemArray != NULL)) { 4735 CFRelease(itemArray); 4736 itemArray = NULL; 4737 } 4738 4739 return itemArray; 4740} 4741#endif // !TARGET_OS_IPHONE 4742 4743//******************************************************************************** 4744// copyPasswordFromKeychain 4745// -------------------------------------- 4746// Given a uniqueID, retrieve the password from the keychain 4747//******************************************************************************** 4748static CFStringRef 4749copyPasswordFromKeychain(CFStringRef uniqueID) 4750{ 4751#if !TARGET_OS_IPHONE 4752 CFArrayRef enumerator; 4753 CFIndex n; 4754 CFStringRef password = NULL; 4755 4756 enumerator = copyKeychainEnumerator(uniqueID); 4757 if (enumerator == NULL) { 4758 return NULL; // if no keychain enumerator 4759 } 4760 4761 n = CFArrayGetCount(enumerator); 4762 if (n > 0) { 4763 void *data = NULL; 4764 UInt32 dataLen = 0; 4765 SecKeychainItemRef itemRef; 4766 OSStatus result; 4767 4768 itemRef = (SecKeychainItemRef)CFArrayGetValueAtIndex(enumerator, 0); 4769 result = SecKeychainItemCopyContent(itemRef, // itemRef 4770 NULL, // itemClass 4771 NULL, // attrList 4772 &dataLen, // length 4773 (void *)&data); // outData 4774 if ((result == noErr) && (data != NULL) && (dataLen > 0)) { 4775 password = CFStringCreateWithBytes(NULL, data, dataLen, kCFStringEncodingUTF8, TRUE); 4776 (void) SecKeychainItemFreeContent(NULL, data); 4777 } 4778 4779 } 4780 4781 CFRelease(enumerator); 4782 4783 return password; 4784#else // !TARGET_OS_IPHONE 4785#pragma unused(uniqueID) 4786 return NULL; 4787#endif // !TARGET_OS_IPHONE 4788} 4789 4790 4791__private_extern__ 4792char * 4793__SCNetworkConnectionGetControllerPortName(void) 4794{ 4795#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000)) && !TARGET_OS_SIMULATOR 4796 if (scnc_server_name == NULL){ 4797 if (!(sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME | SANDBOX_CHECK_NO_REPORT, PPPCONTROLLER_SERVER_PRIV))){ 4798 scnc_server_name = PPPCONTROLLER_SERVER_PRIV; 4799 } else { 4800 scnc_server_name = PPPCONTROLLER_SERVER; 4801 } 4802 } 4803#else 4804 scnc_server_name = PPPCONTROLLER_SERVER; 4805#endif 4806 return scnc_server_name; 4807} 4808