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