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