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