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