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