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