]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCNetworkConnection.c
configd-453.16.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCNetworkConnection.c
1 /*
2 * Copyright (c) 2003-2012 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
48 #if !TARGET_OS_IPHONE
49 #include <Security/Security.h>
50 #include "dy_framework.h"
51 #endif // !TARGET_OS_IPHONE
52
53 #include <servers/bootstrap.h>
54 #include <bootstrap_priv.h>
55
56 #include <pthread.h>
57 #include <notify.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <netdb.h>
61 #include <unistd.h>
62 #include <sys/ioctl.h>
63 #include <sys/socket.h>
64 #include <net/if.h>
65 #include <mach/mach.h>
66 #include <bsm/audit.h>
67
68 #include <ppp/ppp_msg.h>
69 #include "pppcontroller.h"
70 #include <ppp/pppcontroller_types.h>
71
72
73
74 static int debug = 0;
75 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
76 static pthread_mutex_t scnc_lock = PTHREAD_MUTEX_INITIALIZER;
77 static mach_port_t scnc_server = MACH_PORT_NULL;
78
79
80 typedef struct {
81
82 /* base CFType information */
83 CFRuntimeBase cfBase;
84
85 /* lock */
86 pthread_mutex_t lock;
87
88 /* service */
89 SCNetworkServiceRef service;
90
91 /* client info (if we are proxying for another process */
92 mach_port_t client_audit_session;
93 uid_t client_uid;
94 gid_t client_gid;
95 pid_t client_pid;
96
97 /* ref to PPP controller for control messages */
98 mach_port_t session_port;
99
100 /* ref to PPP controller for notification messages */
101 CFMachPortRef notify_port;
102
103 /* keep track of whether we're acquired the initial status */
104 Boolean haveStatus;
105
106 /* run loop source, callout, context, rl scheduling info */
107 Boolean scheduled;
108 CFRunLoopSourceRef rls;
109 SCNetworkConnectionCallBack rlsFunction;
110 SCNetworkConnectionContext rlsContext;
111 CFMutableArrayRef rlList;
112
113 /* SCNetworkConnectionSetDispatchQueue */
114 dispatch_group_t dispatchGroup;
115 dispatch_queue_t dispatchQueue;
116 dispatch_source_t dispatchSource;
117
118 } SCNetworkConnectionPrivate, *SCNetworkConnectionPrivateRef;
119
120
121 static __inline__ CFTypeRef
122 isA_SCNetworkConnection(CFTypeRef obj)
123 {
124 return (isA_CFType(obj, SCNetworkConnectionGetTypeID()));
125 }
126
127
128 static CFStringRef
129 __SCNetworkConnectionCopyDescription(CFTypeRef cf)
130 {
131 CFAllocatorRef allocator = CFGetAllocator(cf);
132 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)cf;
133 CFMutableStringRef result;
134
135 result = CFStringCreateMutable(allocator, 0);
136 CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkConnection, %p [%p]> {"), cf, allocator);
137 CFStringAppendFormat(result, NULL, CFSTR("service = %p"), connectionPrivate->service);
138 if (connectionPrivate->session_port != MACH_PORT_NULL) {
139 CFStringAppendFormat(result, NULL, CFSTR(", server port = %p"), connectionPrivate->session_port);
140 }
141 CFStringAppendFormat(result, NULL, CFSTR("}"));
142
143 return result;
144 }
145
146
147 static void
148 __SCNetworkConnectionDeallocate(CFTypeRef cf)
149 {
150 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)cf;
151
152 /* release resources */
153 pthread_mutex_destroy(&connectionPrivate->lock);
154
155 if (connectionPrivate->client_audit_session != MACH_PORT_NULL) {
156 mach_port_mod_refs(mach_task_self(),
157 connectionPrivate->client_audit_session,
158 MACH_PORT_RIGHT_SEND,
159 -1);
160 }
161
162 if (connectionPrivate->rls != NULL) {
163 CFRunLoopSourceInvalidate(connectionPrivate->rls);
164 CFRelease(connectionPrivate->rls);
165 }
166
167 if (connectionPrivate->rlList != NULL) {
168 CFRelease(connectionPrivate->rlList);
169 }
170
171 if (connectionPrivate->notify_port != NULL) {
172 mach_port_t mp = CFMachPortGetPort(connectionPrivate->notify_port);
173
174 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate notify_port", mp);
175 CFMachPortInvalidate(connectionPrivate->notify_port);
176 CFRelease(connectionPrivate->notify_port);
177 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
178 }
179
180 if (connectionPrivate->session_port != MACH_PORT_NULL) {
181 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionDeallocate session_port", connectionPrivate->session_port);
182 (void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
183 }
184
185 if (connectionPrivate->rlsContext.release != NULL)
186 (*connectionPrivate->rlsContext.release)(connectionPrivate->rlsContext.info);
187
188 CFRelease(connectionPrivate->service);
189
190 return;
191 }
192
193
194 static CFTypeID __kSCNetworkConnectionTypeID = _kCFRuntimeNotATypeID;
195
196 static const CFRuntimeClass __SCNetworkConnectionClass = {
197 0, // version
198 "SCNetworkConnection", // className
199 NULL, // init
200 NULL, // copy
201 __SCNetworkConnectionDeallocate, // dealloc
202 NULL, // equal
203 NULL, // hash
204 NULL, // copyFormattingDesc
205 __SCNetworkConnectionCopyDescription // copyDebugDesc
206 };
207
208
209 static void
210 childForkHandler()
211 {
212 /* the process has forked (and we are the child process) */
213
214 scnc_server = MACH_PORT_NULL;
215 return;
216 }
217
218
219 static void
220 __SCNetworkConnectionInitialize(void)
221 {
222 char *env;
223
224 /* get the debug environment variable */
225 env = getenv("PPPDebug");
226 if (env != NULL) {
227 if (sscanf(env, "%d", &debug) != 1) {
228 /* PPPDebug value is not valid (or non-numeric), set debug to 1 */
229 debug = 1;
230 }
231 }
232
233 /* register with CoreFoundation */
234 __kSCNetworkConnectionTypeID = _CFRuntimeRegisterClass(&__SCNetworkConnectionClass);
235
236 /* add handler to cleanup after fork() */
237 (void) pthread_atfork(NULL, NULL, childForkHandler);
238
239 return;
240 }
241
242
243 static Boolean
244 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection);
245
246
247 static void
248 __SCNetworkConnectionCallBack(CFMachPortRef port, void * msg, CFIndex size, void * info)
249 {
250 mach_no_senders_notification_t *buf = msg;
251 mach_msg_id_t msgid = buf->not_header.msgh_id;
252
253 SCNetworkConnectionRef connection = (SCNetworkConnectionRef)info;
254 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
255 void *context_info;
256 void (*context_release)(const void *);
257 SCNetworkConnectionCallBack rlsFunction = NULL;
258 SCNetworkConnectionStatus nc_status = kSCNetworkConnectionInvalid;
259
260 if (msgid == MACH_NOTIFY_NO_SENDERS) {
261 // re-establish notification
262 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCNetworkConnectionCallBack: PPPController server died"));
263 (void)__SCNetworkConnectionReconnectNotifications(connection);
264 }
265
266 pthread_mutex_lock(&connectionPrivate->lock);
267
268 if (!connectionPrivate->scheduled) {
269 // if not currently scheduled
270 goto doit;
271 }
272
273 rlsFunction = connectionPrivate->rlsFunction;
274 if (rlsFunction == NULL) {
275 goto doit;
276 }
277
278 if ((connectionPrivate->rlsContext.retain != NULL) && (connectionPrivate->rlsContext.info != NULL)) {
279 context_info = (void *)(*connectionPrivate->rlsContext.retain)(connectionPrivate->rlsContext.info);
280 context_release = connectionPrivate->rlsContext.release;
281 } else {
282 context_info = connectionPrivate->rlsContext.info;
283 context_release = NULL;
284 }
285
286 doit :
287
288 pthread_mutex_unlock(&connectionPrivate->lock);
289
290 if (rlsFunction == NULL) {
291 return;
292 }
293
294 nc_status = SCNetworkConnectionGetStatus(connection);
295 (*rlsFunction)(connection, nc_status, context_info);
296 if ((context_release != NULL) && (context_info != NULL)) {
297 (*context_release)(context_info);
298 }
299
300 return;
301 }
302
303
304 #pragma mark -
305 #pragma mark SCNetworkConnection APIs
306
307
308 static CFStringRef
309 pppMPCopyDescription(const void *info)
310 {
311 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)info;
312
313 return CFStringCreateWithFormat(NULL,
314 NULL,
315 CFSTR("<SCNetworkConnection MP %p> {service = %@, callout = %p}"),
316 connectionPrivate,
317 connectionPrivate->service,
318 connectionPrivate->rlsFunction);
319 }
320
321
322 static SCNetworkConnectionPrivateRef
323 __SCNetworkConnectionCreatePrivate(CFAllocatorRef allocator,
324 SCNetworkServiceRef service,
325 SCNetworkConnectionCallBack callout,
326 SCNetworkConnectionContext *context)
327 {
328 SCNetworkConnectionPrivateRef connectionPrivate = NULL;
329 uint32_t size;
330
331
332 /* initialize runtime */
333 pthread_once(&initialized, __SCNetworkConnectionInitialize);
334
335 /* allocate NetworkConnection */
336 size = sizeof(SCNetworkConnectionPrivate) - sizeof(CFRuntimeBase);
337 connectionPrivate = (SCNetworkConnectionPrivateRef)_CFRuntimeCreateInstance(allocator, __kSCNetworkConnectionTypeID, size, NULL);
338 if (connectionPrivate == NULL) {
339 goto fail;
340 }
341
342 /* zero the data structure */
343 bzero(((u_char*)connectionPrivate)+sizeof(CFRuntimeBase), size);
344
345 pthread_mutex_init(&connectionPrivate->lock, NULL);
346
347 /* save the service */
348 connectionPrivate->service = CFRetain(service);
349
350 connectionPrivate->client_audit_session = MACH_PORT_NULL;
351 connectionPrivate->client_uid = geteuid();
352 connectionPrivate->client_gid = getegid();
353 connectionPrivate->client_pid = getpid();
354
355 connectionPrivate->rlsFunction = callout;
356
357 if (context) {
358 bcopy(context, &connectionPrivate->rlsContext, sizeof(SCNetworkConnectionContext));
359 if (context->retain != NULL) {
360 connectionPrivate->rlsContext.info = (void *)(*context->retain)(context->info);
361 }
362 }
363
364 /* success, return the connection reference */
365 return connectionPrivate;
366
367 fail:
368
369 /* failure, clean up and leave */
370 if (connectionPrivate != NULL) {
371 CFRelease(connectionPrivate);
372 }
373
374 _SCErrorSet(kSCStatusFailed);
375 return NULL;
376 }
377
378
379 static mach_port_t
380 __SCNetworkConnectionServerPort(kern_return_t *status)
381 {
382 mach_port_t server = MACH_PORT_NULL;
383
384 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
385 *status = bootstrap_look_up2(bootstrap_port,
386 PPPCONTROLLER_SERVER,
387 &server,
388 0,
389 BOOTSTRAP_PRIVILEGED_SERVER);
390 #else // BOOTSTRAP_PRIVILEGED_SERVER
391 *status = bootstrap_look_up(bootstrap_port, PPPCONTROLLER_SERVER, &server);
392 #endif // BOOTSTRAP_PRIVILEGED_SERVER
393
394 switch (*status) {
395 case BOOTSTRAP_SUCCESS :
396 // service currently registered, "a good thing" (tm)
397 return server;
398 case BOOTSTRAP_NOT_PRIVILEGED :
399 // the service is not privileged
400 break;
401 case BOOTSTRAP_UNKNOWN_SERVICE :
402 // service not currently registered, try again later
403 break;
404 default :
405 #ifdef DEBUG
406 SCLog(_sc_verbose, LOG_DEBUG,
407 CFSTR("SCNetworkConnection bootstrap_look_up() failed: status=%s"),
408 bootstrap_strerror(*status));
409 #endif // DEBUG
410 break;
411 }
412
413 return MACH_PORT_NULL;
414 }
415
416
417 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_IPHONE_SIMULATOR
418 #define HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
419 #endif
420
421
422 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) && !TARGET_IPHONE_SIMULATOR
423 #define HAVE_PPPCONTROLLER_ATTACHWITHPROXY
424 #endif
425
426
427 static mach_port_t
428 __SCNetworkConnectionSessionPort(SCNetworkConnectionPrivateRef connectionPrivate)
429 {
430 void *data = NULL;
431 CFIndex dataLen = 0;
432 CFDataRef dataRef = NULL;
433 mach_port_t notify_port = MACH_PORT_NULL;
434 mach_port_t oldNotify = MACH_PORT_NULL;
435 int retry = 0;
436 int sc_status = kSCStatusFailed;
437 mach_port_t server = scnc_server;
438 kern_return_t status = KERN_SUCCESS;
439
440 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
441 mach_port_t au_session = MACH_PORT_NULL;
442 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
443
444 if (connectionPrivate->session_port != MACH_PORT_NULL) {
445 return connectionPrivate->session_port;
446 }
447
448 if (!_SCSerializeString(SCNetworkServiceGetServiceID(connectionPrivate->service), &dataRef, &data, &dataLen)) {
449 goto done;
450 }
451
452 if (connectionPrivate->notify_port != NULL) {
453 mach_port_t mp = CFMachPortGetPort(connectionPrivate->notify_port);
454
455 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionReconnectNotifications mp", mp);
456 CFMachPortInvalidate(connectionPrivate->notify_port);
457 CFRelease(connectionPrivate->notify_port);
458 connectionPrivate->notify_port = NULL;
459 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
460 }
461
462 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
463 au_session = audit_session_self();
464 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
465
466 // open a new session with the server
467 while (TRUE) {
468 if ((connectionPrivate->rlsFunction != NULL) && (notify_port == MACH_PORT_NULL)) {
469 // allocate port (for server response)
470 status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &notify_port);
471 if (status != KERN_SUCCESS) {
472 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort mach_port_allocate(): %s"), mach_error_string(status));
473 sc_status = status;
474 goto done;
475 }
476
477 // add send right (passed to the server)
478 status = mach_port_insert_right(mach_task_self(),
479 notify_port,
480 notify_port,
481 MACH_MSG_TYPE_MAKE_SEND);
482 if (status != KERN_SUCCESS) {
483 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort mach_port_insert_right(): %s"), mach_error_string(status));
484 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
485 sc_status = status;
486 goto done;
487 }
488 }
489
490 if (server != MACH_PORT_NULL) {
491 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
492 if ((connectionPrivate->client_audit_session == MACH_PORT_NULL) &&
493 (connectionPrivate->client_uid == geteuid()) &&
494 (connectionPrivate->client_gid == getegid()) &&
495 (connectionPrivate->client_pid == getpid())
496 ) {
497 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
498 status = pppcontroller_attach(server,
499 data,
500 dataLen,
501 bootstrap_port,
502 notify_port,
503 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
504 au_session,
505 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
506 &connectionPrivate->session_port,
507 &sc_status);
508 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHPROXY
509 } else {
510 mach_port_t client_au_session;
511
512 if (connectionPrivate->client_audit_session == MACH_PORT_NULL) {
513 client_au_session = au_session;
514 } else {
515 client_au_session = connectionPrivate->client_audit_session;
516 }
517
518 status = pppcontroller_attach_proxy(server,
519 data,
520 dataLen,
521 bootstrap_port,
522 notify_port,
523 client_au_session,
524 connectionPrivate->client_uid,
525 connectionPrivate->client_gid,
526 connectionPrivate->client_pid,
527 &connectionPrivate->session_port,
528 &sc_status);
529 }
530 #endif // HAVE_PPPCONTROLLER_ATTACHWITHPROXY
531 if (status == KERN_SUCCESS) {
532 if (sc_status != kSCStatusOK) {
533 SCLog(TRUE, LOG_DEBUG,
534 CFSTR("__SCNetworkConnectionSessionPort : attach w/error, sc_status=%s%s"),
535 SCErrorString(sc_status),
536 (connectionPrivate->session_port != MACH_PORT_NULL) ? ", w/session_port!=MACH_PORT_NULL" : "");
537
538 if (connectionPrivate->session_port != MACH_PORT_NULL) {
539 __MACH_PORT_DEBUG(TRUE,
540 "*** __SCNetworkConnectionSessionPort session_port (attach w/error, cleanup)",
541 connectionPrivate->session_port);
542 mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
543 connectionPrivate->session_port = MACH_PORT_NULL;
544 }
545
546 if (notify_port != MACH_PORT_NULL) {
547 __MACH_PORT_DEBUG(TRUE,
548 "*** __SCNetworkConnectionSessionPort notify_port (attach w/error, cleanup)",
549 notify_port);
550 (void) mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
551 notify_port = MACH_PORT_NULL;
552 }
553 }
554 break;
555 }
556
557 // our [cached] server port is not valid
558 SCLog(TRUE, LOG_DEBUG, CFSTR("__SCNetworkConnectionSessionPort : !attach: %s"), SCErrorString(status));
559 if (status == MACH_SEND_INVALID_DEST) {
560 // the server is not yet available
561 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!dest)", notify_port);
562 } else if (status == MIG_SERVER_DIED) {
563 __MACH_PORT_DEBUG(TRUE, "*** __SCNetworkConnectionSessionPort notify_port (!mig)", notify_port);
564 // the server we were using is gone and we've lost our send right
565 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
566 notify_port = MACH_PORT_NULL;
567 } else {
568 // if we got an unexpected error, don't retry
569 sc_status = status;
570 break;
571 }
572 }
573
574 pthread_mutex_lock(&scnc_lock);
575 if (scnc_server != MACH_PORT_NULL) {
576 if (server == scnc_server) {
577 // if the server we tried returned the error
578 (void)mach_port_deallocate(mach_task_self(), scnc_server);
579 scnc_server = __SCNetworkConnectionServerPort(&sc_status);
580 } else {
581 // another thread has refreshed the server port
582 }
583 } else {
584 scnc_server = __SCNetworkConnectionServerPort(&sc_status);
585 }
586 server = scnc_server;
587 pthread_mutex_unlock(&scnc_lock);
588
589 if (server == MACH_PORT_NULL) {
590 // if server not available
591 if (sc_status == BOOTSTRAP_UNKNOWN_SERVICE) {
592 // if first retry attempt, wait for SCDynamicStore server
593 if (retry == 0) {
594 SCDynamicStoreRef store;
595
596 store = SCDynamicStoreCreate(NULL,
597 CFSTR("SCNetworkConnection connect"),
598 NULL,
599 NULL);
600 if (store != NULL) {
601 CFRelease(store);
602 }
603 }
604
605 // wait up to 2.5 seconds for the [SCNetworkConnection] server
606 // to startup
607 if ((retry += 50) < 2500) {
608 usleep(50 * 1000); // sleep 50ms between attempts
609 continue;
610 }
611 }
612 break;
613 }
614 }
615
616 if (notify_port != MACH_PORT_NULL) {
617 if (connectionPrivate->session_port != MACH_PORT_NULL) {
618 CFMachPortContext context = { 0
619 , (void *)connectionPrivate
620 , NULL
621 , NULL
622 , pppMPCopyDescription
623 };
624
625 // request a notification when/if the server dies
626 status = mach_port_request_notification(mach_task_self(),
627 notify_port,
628 MACH_NOTIFY_NO_SENDERS,
629 1,
630 notify_port,
631 MACH_MSG_TYPE_MAKE_SEND_ONCE,
632 &oldNotify);
633 if (status != KERN_SUCCESS) {
634 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort mach_port_request_notification(): %s"), mach_error_string(status));
635 mach_port_mod_refs(mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
636 sc_status = status;
637 goto done;
638 }
639
640 if (oldNotify != MACH_PORT_NULL) {
641 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionSessionPort(): oldNotify != MACH_PORT_NULL"));
642 }
643
644 // create CFMachPort for SCNetworkConnection notification callback
645 connectionPrivate->notify_port = _SC_CFMachPortCreateWithPort("SCNetworkConnection",
646 notify_port,
647 __SCNetworkConnectionCallBack,
648 &context);
649
650 // we need to try a bit harder to acquire the initial status
651 connectionPrivate->haveStatus = FALSE;
652 } else {
653 // with no server port, release the notification port we allocated
654 __MACH_PORT_DEBUG(TRUE,
655 "*** __SCNetworkConnectionSessionPort notify_port (!server)",
656 notify_port);
657 (void) mach_port_mod_refs (mach_task_self(), notify_port, MACH_PORT_RIGHT_RECEIVE, -1);
658 (void) mach_port_deallocate(mach_task_self(), notify_port);
659 notify_port = MACH_PORT_NULL;
660 }
661 }
662
663 done :
664
665 // clean up
666
667 #ifdef HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
668 if (au_session != MACH_PORT_NULL){
669 (void)mach_port_deallocate(mach_task_self(), au_session);
670 }
671 #endif // HAVE_PPPCONTROLLER_ATTACHWITHAUDITSESSION
672
673 if (dataRef != NULL) CFRelease(dataRef);
674
675 switch (sc_status) {
676 case kSCStatusOK :
677 __MACH_PORT_DEBUG(connectionPrivate->session_port != MACH_PORT_NULL,
678 "*** __SCNetworkConnectionSessionPort session_port",
679 connectionPrivate->session_port);
680 __MACH_PORT_DEBUG(notify_port != MACH_PORT_NULL,
681 "*** __SCNetworkConnectionSessionPort notify_port",
682 notify_port);
683 break;
684 case BOOTSTRAP_UNKNOWN_SERVICE :
685 SCLog(TRUE,
686 (status == KERN_SUCCESS) ? LOG_DEBUG : LOG_ERR,
687 CFSTR("PPPController not available"));
688 break;
689 default :
690 SCLog(TRUE,
691 (status == KERN_SUCCESS) ? LOG_DEBUG : LOG_ERR,
692 CFSTR("__SCNetworkConnectionSessionPort pppcontroller_attach(): %s"),
693 SCErrorString(sc_status));
694 break;
695 }
696
697 if (sc_status != kSCStatusOK) {
698 _SCErrorSet(sc_status);
699 }
700
701 return connectionPrivate->session_port;
702 }
703
704
705 static Boolean
706 __SCNetworkConnectionReconnect(SCNetworkConnectionRef connection)
707 {
708 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
709 mach_port_t port;
710
711 port = __SCNetworkConnectionSessionPort(connectionPrivate);
712 return (port != MACH_PORT_NULL);
713 }
714
715
716 static Boolean
717 __SCNetworkConnectionReconnectNotifications(SCNetworkConnectionRef connection)
718 {
719 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
720 dispatch_group_t dispatchGroup = NULL;
721 dispatch_queue_t dispatchQueue = NULL;
722 Boolean ok = TRUE;
723 CFArrayRef rlList = NULL;
724
725 // Before we fully tearing down our [old] notifications, make sure
726 // we have retained any information that is needed to re-register the
727 // [new] notifications.
728
729 pthread_mutex_lock(&connectionPrivate->lock);
730
731 if (connectionPrivate->rlList != NULL) {
732 rlList = CFArrayCreateCopy(NULL, connectionPrivate->rlList);
733 }
734 if (connectionPrivate->dispatchQueue != NULL) {
735 // save dispatchQueue, release reference when we've queue'd blocks
736 // complete, allow re-scheduling
737 dispatchGroup = connectionPrivate->dispatchGroup;
738 connectionPrivate->dispatchGroup = NULL;
739 dispatchQueue = connectionPrivate->dispatchQueue;
740 connectionPrivate->dispatchQueue = NULL;
741
742 // and take an extra reference for rescheduling
743 dispatch_retain(dispatchQueue);
744 }
745
746 // cancel [old] notifications
747 if (connectionPrivate->rlList != NULL) {
748 CFRelease(connectionPrivate->rlList);
749 connectionPrivate->rlList = NULL;
750 }
751 if (connectionPrivate->rls != NULL) {
752 CFRunLoopSourceInvalidate(connectionPrivate->rls);
753 CFRelease(connectionPrivate->rls);
754 connectionPrivate->rls = NULL;
755 }
756 if (connectionPrivate->dispatchSource != NULL) {
757 dispatch_source_cancel(connectionPrivate->dispatchSource);
758 connectionPrivate->dispatchSource = NULL;
759 }
760
761 connectionPrivate->scheduled = FALSE;
762
763 pthread_mutex_unlock(&connectionPrivate->lock);
764
765 if (dispatchGroup != NULL) {
766 dispatch_group_notify(dispatchGroup, dispatchQueue, ^{
767 // release group/queue references
768 dispatch_release(dispatchQueue);
769 dispatch_release(dispatchGroup); // releases our connection reference
770 });
771 }
772
773 // re-schedule
774 if (rlList != NULL) {
775 CFIndex i;
776 CFIndex n;
777
778 n = CFArrayGetCount(rlList);
779 for (i = 0; i < n; i += 3) {
780 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlList, i+1);
781 CFStringRef rlMode = (CFStringRef) CFArrayGetValueAtIndex(rlList, i+2);
782
783 ok = SCNetworkConnectionScheduleWithRunLoop(connection, rl, rlMode);
784 if (!ok) {
785 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE),
786 LOG_ERR,
787 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionScheduleWithRunLoop() failed"));
788 goto done;
789 }
790 }
791 } else if (dispatchQueue != NULL) {
792 ok = SCNetworkConnectionSetDispatchQueue(connection, dispatchQueue);
793 if (!ok) {
794 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE),
795 LOG_ERR,
796 CFSTR("__SCNetworkConnectionReconnectNotifications: SCNetworkConnectionSetDispatchQueue() failed"));
797 goto done;
798 }
799 } else {
800 ok = FALSE;
801 }
802
803 done :
804
805 // cleanup
806 if (rlList != NULL) {
807 CFRelease(rlList);
808 }
809 if (dispatchQueue != NULL) {
810 dispatch_release(dispatchQueue);
811 }
812
813 if (!ok) {
814 SCLog(TRUE, LOG_ERR,
815 CFSTR("SCNetworkConnection server %s, notification not restored"),
816 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE) ? "shutdown" : "failed");
817 }
818
819 return ok;
820 }
821
822
823 static Boolean
824 __SCNetworkConnectionNeedsRetry(SCNetworkConnectionRef connection,
825 const char *error_label,
826 kern_return_t status,
827 int *sc_status)
828 {
829 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
830
831 if (status == KERN_SUCCESS) {
832 return FALSE;
833 }
834
835 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
836 // the server's gone and our session port's dead, remove the dead name right
837 (void) mach_port_deallocate(mach_task_self(), connectionPrivate->session_port);
838 } else {
839 // we got an unexpected error, leave the [session] port alone
840 SCLog(TRUE, LOG_ERR, CFSTR("%s: %s"), error_label, mach_error_string(status));
841 }
842 connectionPrivate->session_port = MACH_PORT_NULL;
843 if ((status == MACH_SEND_INVALID_DEST) || (status == MIG_SERVER_DIED)) {
844 if (__SCNetworkConnectionReconnect(connection)) {
845 return TRUE;
846 }
847 }
848 *sc_status = status;
849
850 return FALSE;
851 }
852
853
854 CFTypeID
855 SCNetworkConnectionGetTypeID(void) {
856 pthread_once(&initialized, __SCNetworkConnectionInitialize); /* initialize runtime */
857 return __kSCNetworkConnectionTypeID;
858 }
859
860
861 CFArrayRef /* of SCNetworkServiceRef's */
862 SCNetworkConnectionCopyAvailableServices(SCNetworkSetRef set)
863 {
864 CFMutableArrayRef available;
865 Boolean tempSet = FALSE;
866
867 if (set == NULL) {
868 SCPreferencesRef prefs;
869
870 prefs = SCPreferencesCreate(NULL, CFSTR("SCNetworkConnectionCopyAvailableServices"), NULL);
871 if (prefs != NULL) {
872 set = SCNetworkSetCopyCurrent(prefs);
873 CFRelease(prefs);
874 }
875 tempSet = TRUE;
876 }
877
878 available = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
879
880 if (set != NULL) {
881 CFArrayRef services;
882
883 services = SCNetworkSetCopyServices(set);
884 if (services != NULL) {
885 CFIndex i;
886 CFIndex n;
887
888 n = CFArrayGetCount(services);
889 for (i = 0; i < n; i++) {
890 SCNetworkInterfaceRef interface;
891 CFStringRef interfaceType;
892 SCNetworkServiceRef service;
893
894 service = CFArrayGetValueAtIndex(services, i);
895 interface = SCNetworkServiceGetInterface(service);
896 if (interface == NULL) {
897 continue;
898 }
899
900 interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
901 if (CFEqual(interfaceType, kSCNetworkInterfaceTypePPP) ||
902 CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) ||
903 CFEqual(interfaceType, kSCNetworkInterfaceTypeIPSec)) {
904 CFArrayAppendValue(available, service);
905 }
906 }
907
908 CFRelease(services);
909 }
910 }
911
912 if (tempSet && (set != NULL)) {
913 CFRelease(set);
914 }
915 return available;
916 }
917
918
919 SCNetworkConnectionRef
920 SCNetworkConnectionCreateWithService(CFAllocatorRef allocator,
921 SCNetworkServiceRef service,
922 SCNetworkConnectionCallBack callout,
923 SCNetworkConnectionContext *context)
924 {
925 SCNetworkConnectionPrivateRef connectionPrivate;
926
927 if (!isA_SCNetworkService(service)) {
928 _SCErrorSet(kSCStatusInvalidArgument);
929 return FALSE;
930 }
931
932 connectionPrivate = __SCNetworkConnectionCreatePrivate(allocator, service, callout, context);
933 return (SCNetworkConnectionRef)connectionPrivate;
934 }
935
936
937 SCNetworkConnectionRef
938 SCNetworkConnectionCreateWithServiceID(CFAllocatorRef allocator,
939 CFStringRef serviceID,
940 SCNetworkConnectionCallBack callout,
941 SCNetworkConnectionContext *context)
942 {
943 SCNetworkConnectionRef connection;
944 SCNetworkServiceRef service;
945
946 if (!isA_CFString(serviceID)) {
947 _SCErrorSet(kSCStatusInvalidArgument);
948 return NULL;
949 }
950
951 service = _SCNetworkServiceCopyActive(NULL, serviceID);
952 if (service == NULL) {
953 return NULL;
954 }
955
956 connection = SCNetworkConnectionCreateWithService(allocator, service, callout, context);
957 CFRelease(service);
958
959 return connection;
960 }
961
962
963 CFStringRef
964 SCNetworkConnectionCopyServiceID(SCNetworkConnectionRef connection)
965 {
966 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
967 CFStringRef serviceID;
968
969 if (!isA_SCNetworkConnection(connection)) {
970 _SCErrorSet(kSCStatusInvalidArgument);
971 return NULL;
972 }
973
974 serviceID = SCNetworkServiceGetServiceID(connectionPrivate->service);
975 return CFRetain(serviceID);
976 }
977
978
979 Boolean
980 SCNetworkConnectionSetClientInfo(SCNetworkConnectionRef connection,
981 mach_port_t client_audit_session,
982 uid_t client_uid,
983 gid_t client_gid,
984 pid_t client_pid)
985 {
986 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
987
988 if (!isA_SCNetworkConnection(connection)) {
989 _SCErrorSet(kSCStatusInvalidArgument);
990 return FALSE;
991 }
992
993 // save client bootstrap port
994 if (connectionPrivate->client_audit_session != MACH_PORT_NULL) {
995 mach_port_mod_refs(mach_task_self(),
996 connectionPrivate->client_audit_session,
997 MACH_PORT_RIGHT_SEND,
998 -1);
999 connectionPrivate->client_audit_session = MACH_PORT_NULL;
1000 }
1001 connectionPrivate->client_audit_session = client_audit_session;
1002 if (connectionPrivate->client_audit_session != MACH_PORT_NULL) {
1003 mach_port_mod_refs(mach_task_self(),
1004 connectionPrivate->client_audit_session,
1005 MACH_PORT_RIGHT_SEND,
1006 1);
1007 }
1008
1009 // save client UID, GID, and PID
1010 connectionPrivate->client_uid = client_uid;
1011 connectionPrivate->client_gid = client_gid;
1012 connectionPrivate->client_pid = client_pid;
1013
1014 return TRUE;
1015 }
1016
1017
1018 CFDictionaryRef
1019 SCNetworkConnectionCopyStatistics(SCNetworkConnectionRef connection)
1020 {
1021 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1022 xmlDataOut_t data = NULL;
1023 mach_msg_type_number_t datalen = 0;
1024 int sc_status = kSCStatusFailed;
1025 mach_port_t session_port;
1026 CFPropertyListRef statistics = NULL;
1027 kern_return_t status;
1028
1029 if (!isA_SCNetworkConnection(connection)) {
1030 _SCErrorSet(kSCStatusInvalidArgument);
1031 return NULL;
1032 }
1033
1034 pthread_mutex_lock(&connectionPrivate->lock);
1035
1036 retry :
1037
1038 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1039 if (session_port == MACH_PORT_NULL) {
1040 goto done;
1041 }
1042
1043 status = pppcontroller_copystatistics(session_port, &data, &datalen, &sc_status);
1044 if (__SCNetworkConnectionNeedsRetry(connection,
1045 "SCNetworkConnectionCopyStatistics()",
1046 status,
1047 &sc_status)) {
1048 goto retry;
1049 }
1050
1051 if (data != NULL) {
1052 if (!_SCUnserialize(&statistics, NULL, data, datalen)) {
1053 if (sc_status != kSCStatusOK) sc_status = SCError();
1054 }
1055 if ((sc_status == kSCStatusOK) && !isA_CFDictionary(statistics)) {
1056 sc_status = kSCStatusFailed;
1057 }
1058 }
1059
1060 if (sc_status != kSCStatusOK) {
1061 if (statistics != NULL) {
1062 CFRelease(statistics);
1063 statistics = NULL;
1064 }
1065 _SCErrorSet(sc_status);
1066 }
1067
1068 done :
1069
1070 pthread_mutex_unlock(&connectionPrivate->lock);
1071 return statistics;
1072 }
1073
1074
1075 SCNetworkServiceRef
1076 SCNetworkConnectionGetService(SCNetworkConnectionRef connection)
1077 {
1078 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1079
1080 if (!isA_SCNetworkConnection(connection)) {
1081 _SCErrorSet(kSCStatusInvalidArgument);
1082 return NULL;
1083 }
1084
1085 return connectionPrivate->service;
1086 }
1087
1088
1089 SCNetworkConnectionStatus
1090 SCNetworkConnectionGetStatus(SCNetworkConnectionRef connection)
1091 {
1092 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1093 SCNetworkConnectionStatus nc_status = kSCNetworkConnectionInvalid;
1094 int retry = 0;
1095 int sc_status = kSCStatusFailed;
1096 mach_port_t session_port;
1097 kern_return_t status;
1098
1099 if (!isA_SCNetworkConnection(connection)) {
1100 _SCErrorSet(kSCStatusInvalidArgument);
1101 return kSCNetworkConnectionInvalid;
1102 }
1103
1104 pthread_mutex_lock(&connectionPrivate->lock);
1105
1106 retry :
1107
1108 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1109 if (session_port == MACH_PORT_NULL) {
1110 nc_status = kSCNetworkConnectionInvalid;
1111 goto done;
1112 }
1113
1114 status = pppcontroller_getstatus(session_port, &nc_status, &sc_status);
1115 if (__SCNetworkConnectionNeedsRetry(connection,
1116 "SCNetworkConnectionGetStatus()",
1117 status,
1118 &sc_status)) {
1119 goto retry;
1120 }
1121
1122 // wait up to 250 ms for the network service to become available
1123 if (!connectionPrivate->haveStatus &&
1124 (sc_status == kSCStatusConnectionNoService) &&
1125 ((retry += 10) < 250)) {
1126 usleep(10 * 1000); // sleep 10ms between attempts
1127 goto retry;
1128 }
1129
1130 if (sc_status == kSCStatusOK) {
1131 connectionPrivate->haveStatus = TRUE;
1132 } else {
1133 _SCErrorSet(sc_status);
1134 nc_status = kSCNetworkConnectionInvalid;
1135 }
1136
1137 done :
1138
1139 pthread_mutex_unlock(&connectionPrivate->lock);
1140 return nc_status;
1141 }
1142
1143
1144 CFDictionaryRef
1145 SCNetworkConnectionCopyExtendedStatus(SCNetworkConnectionRef connection)
1146 {
1147 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1148 xmlDataOut_t data = NULL;
1149 mach_msg_type_number_t datalen = 0;
1150 CFPropertyListRef extstatus = NULL;
1151 int retry = 0;
1152 int sc_status = kSCStatusFailed;
1153 mach_port_t session_port;
1154 kern_return_t status;
1155
1156 if (!isA_SCNetworkConnection(connection)) {
1157 _SCErrorSet(kSCStatusInvalidArgument);
1158 return NULL;
1159 }
1160
1161 pthread_mutex_lock(&connectionPrivate->lock);
1162
1163 retry :
1164
1165 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1166 if (session_port == MACH_PORT_NULL) {
1167 goto done;
1168 }
1169
1170 status = pppcontroller_copyextendedstatus(session_port, &data, &datalen, &sc_status);
1171 if (__SCNetworkConnectionNeedsRetry(connection,
1172 "SCNetworkConnectionCopyExtendedStatus()",
1173 status,
1174 &sc_status)) {
1175 goto retry;
1176 }
1177
1178 if (data != NULL) {
1179 if (!_SCUnserialize(&extstatus, NULL, data, datalen)) {
1180 if (sc_status != kSCStatusOK) sc_status = SCError();
1181 }
1182 if ((sc_status == kSCStatusOK) && !isA_CFDictionary(extstatus)) {
1183 sc_status = kSCStatusFailed;
1184 }
1185 }
1186
1187 // wait up to 250 ms for the network service to become available
1188 if (!connectionPrivate->haveStatus &&
1189 (sc_status == kSCStatusConnectionNoService) &&
1190 ((retry += 10) < 250)) {
1191 usleep(10 * 1000); // sleep 10ms between attempts
1192 goto retry;
1193 }
1194
1195 if (sc_status == kSCStatusOK) {
1196 connectionPrivate->haveStatus = TRUE;
1197 } else {
1198 if (extstatus != NULL) {
1199 CFRelease(extstatus);
1200 extstatus = NULL;
1201 }
1202 _SCErrorSet(sc_status);
1203 }
1204
1205 done :
1206
1207 pthread_mutex_unlock(&connectionPrivate->lock);
1208 return extstatus;
1209 }
1210
1211
1212 Boolean
1213 SCNetworkConnectionStart(SCNetworkConnectionRef connection,
1214 CFDictionaryRef userOptions,
1215 Boolean linger)
1216 {
1217 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1218 CFDataRef dataref = NULL;
1219 void *data = NULL;
1220 CFIndex datalen = 0;
1221 Boolean ok = FALSE;
1222 int sc_status = kSCStatusFailed;
1223 mach_port_t session_port;
1224 kern_return_t status;
1225
1226 if (!isA_SCNetworkConnection(connection)) {
1227 _SCErrorSet(kSCStatusInvalidArgument);
1228 return FALSE;
1229 }
1230
1231 if ((userOptions != NULL) && !isA_CFDictionary(userOptions)) {
1232 _SCErrorSet(kSCStatusInvalidArgument);
1233 return FALSE;
1234 }
1235
1236 if (debug > 0) {
1237 CFMutableDictionaryRef mdict = NULL;
1238
1239 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStart (0x%x)"), connectionPrivate);
1240
1241 if (userOptions != NULL) {
1242 CFDictionaryRef dict;
1243 CFStringRef encryption;
1244 CFMutableDictionaryRef new_dict;
1245
1246 /* special code to remove secret information */
1247 mdict = CFDictionaryCreateMutableCopy(NULL, 0, userOptions);
1248
1249 dict = CFDictionaryGetValue(mdict, kSCEntNetPPP);
1250 if (isA_CFDictionary(dict)) {
1251 encryption = CFDictionaryGetValue(dict, kSCPropNetPPPAuthPasswordEncryption);
1252 if (!isA_CFString(encryption) ||
1253 !CFEqual(encryption, kSCValNetPPPAuthPasswordEncryptionKeychain)) {
1254 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1255 CFDictionaryReplaceValue(new_dict, kSCPropNetPPPAuthPassword, CFSTR("******"));
1256 CFDictionarySetValue(mdict, kSCEntNetPPP, new_dict);
1257 CFRelease(new_dict);
1258 }
1259 }
1260
1261 dict = CFDictionaryGetValue(mdict, kSCEntNetL2TP);
1262 if (isA_CFDictionary(dict)) {
1263 encryption = CFDictionaryGetValue(dict, kSCPropNetL2TPIPSecSharedSecretEncryption);
1264 if (!isA_CFString(encryption) ||
1265 !CFEqual(encryption, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain)) {
1266 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1267 CFDictionaryReplaceValue(new_dict, kSCPropNetL2TPIPSecSharedSecret, CFSTR("******"));
1268 CFDictionarySetValue(mdict, kSCEntNetL2TP, new_dict);
1269 CFRelease(new_dict);
1270 }
1271 }
1272
1273 dict = CFDictionaryGetValue(mdict, kSCEntNetIPSec);
1274 if (isA_CFDictionary(dict)) {
1275 encryption = CFDictionaryGetValue(dict, kSCPropNetIPSecSharedSecretEncryption);
1276 if (!isA_CFString(encryption) ||
1277 !CFEqual(encryption, kSCValNetIPSecSharedSecretEncryptionKeychain)) {
1278 new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1279 CFDictionaryReplaceValue(new_dict, kSCPropNetIPSecSharedSecret, CFSTR("******"));
1280 CFDictionarySetValue(mdict, kSCEntNetIPSec, new_dict);
1281 CFRelease(new_dict);
1282 }
1283 }
1284 }
1285
1286 SCLog(TRUE, LOG_DEBUG, CFSTR("User options: %@"), mdict);
1287 if (mdict != NULL) CFRelease(mdict);
1288 }
1289
1290 if (userOptions && !_SCSerialize(userOptions, &dataref, &data, &datalen)) {
1291 return FALSE;
1292 }
1293
1294 pthread_mutex_lock(&connectionPrivate->lock);
1295
1296 retry :
1297
1298 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1299 if (session_port == MACH_PORT_NULL) {
1300 if (dataref) CFRelease(dataref);
1301 goto done;
1302 }
1303
1304 status = pppcontroller_start(session_port, data, datalen, linger, &sc_status);
1305 if (__SCNetworkConnectionNeedsRetry(connection,
1306 "SCNetworkConnectionStart()",
1307 status,
1308 &sc_status)) {
1309 goto retry;
1310 }
1311
1312 if (dataref) CFRelease(dataref);
1313
1314 if (debug > 0) {
1315 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStart (0x%x), return: %d"), connectionPrivate, sc_status);
1316 }
1317
1318 if (sc_status != kSCStatusOK) {
1319 _SCErrorSet(sc_status);
1320 goto done;
1321 }
1322
1323 /* connection is now started */
1324 ok = TRUE;
1325
1326 done:
1327 pthread_mutex_unlock(&connectionPrivate->lock);
1328 return ok;
1329 }
1330
1331
1332 Boolean
1333 SCNetworkConnectionStop(SCNetworkConnectionRef connection,
1334 Boolean forceDisconnect)
1335 {
1336 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1337 Boolean ok = FALSE;
1338 int sc_status = kSCStatusFailed;
1339 mach_port_t session_port;
1340 kern_return_t status;
1341
1342 if (!isA_SCNetworkConnection(connection)) {
1343 _SCErrorSet(kSCStatusInvalidArgument);
1344 return FALSE;
1345 }
1346
1347 if (debug > 0) {
1348 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStop (0x%x)"), connectionPrivate);
1349 }
1350
1351 pthread_mutex_lock(&connectionPrivate->lock);
1352
1353 retry :
1354
1355 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1356 if (session_port == MACH_PORT_NULL) {
1357 goto done;
1358 }
1359
1360 status = pppcontroller_stop(session_port, forceDisconnect, &sc_status);
1361 if (__SCNetworkConnectionNeedsRetry(connection,
1362 "SCNetworkConnectionStop()",
1363 status,
1364 &sc_status)) {
1365 goto retry;
1366 }
1367
1368 if (debug > 0) {
1369 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionStop (0x%x), return: %d"), connectionPrivate, sc_status);
1370 }
1371
1372 if (sc_status != kSCStatusOK) {
1373 _SCErrorSet(sc_status);
1374 goto done;
1375 }
1376
1377 /* connection is now disconnecting */
1378 ok = TRUE;
1379
1380 done :
1381
1382 pthread_mutex_unlock(&connectionPrivate->lock);
1383 return ok;
1384 }
1385
1386
1387 Boolean
1388 SCNetworkConnectionSuspend(SCNetworkConnectionRef connection)
1389 {
1390 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1391 Boolean ok = FALSE;
1392 int sc_status = kSCStatusFailed;
1393 mach_port_t session_port;
1394 kern_return_t status;
1395
1396 if (!isA_SCNetworkConnection(connection)) {
1397 _SCErrorSet(kSCStatusInvalidArgument);
1398 return FALSE;
1399 }
1400
1401 if (debug > 0) {
1402 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionSuspend (0x%x)"), connectionPrivate);
1403 }
1404
1405 pthread_mutex_lock(&connectionPrivate->lock);
1406
1407 retry :
1408
1409 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1410 if (session_port == MACH_PORT_NULL) {
1411 goto done;
1412 }
1413
1414 status = pppcontroller_suspend(session_port, &sc_status);
1415 if (__SCNetworkConnectionNeedsRetry(connection,
1416 "SCNetworkConnectionSuspend()",
1417 status,
1418 &sc_status)) {
1419 goto retry;
1420 }
1421
1422 if (debug > 0) {
1423 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionSuspend (0x%x), return: %d"), connectionPrivate, sc_status);
1424 }
1425
1426 if (sc_status != kSCStatusOK) {
1427 _SCErrorSet(sc_status);
1428 goto done;
1429 }
1430
1431 /* connection is now suspended */
1432 ok = TRUE;
1433
1434 done :
1435
1436 pthread_mutex_unlock(&connectionPrivate->lock);
1437 return ok;
1438 }
1439
1440
1441 Boolean
1442 SCNetworkConnectionResume(SCNetworkConnectionRef connection)
1443 {
1444 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1445 Boolean ok = FALSE;
1446 int sc_status = kSCStatusFailed;
1447 mach_port_t session_port;
1448 kern_return_t status;
1449
1450 if (!isA_SCNetworkConnection(connection)) {
1451 _SCErrorSet(kSCStatusInvalidArgument);
1452 return FALSE;
1453 }
1454
1455 if (debug > 0) {
1456 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionResume (0x%x)"), connectionPrivate);
1457 }
1458
1459 pthread_mutex_lock(&connectionPrivate->lock);
1460
1461 retry :
1462
1463 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1464 if (session_port == MACH_PORT_NULL) {
1465 goto done;
1466 }
1467
1468 status = pppcontroller_resume(session_port, &sc_status);
1469 if (__SCNetworkConnectionNeedsRetry(connection,
1470 "SCNetworkConnectionResume()",
1471 status,
1472 &sc_status)) {
1473 goto retry;
1474 }
1475
1476 if (debug > 0) {
1477 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionResume (0x%x), return: %d"), connectionPrivate, sc_status);
1478 }
1479
1480 if (sc_status != kSCStatusOK) {
1481 _SCErrorSet(sc_status);
1482 goto done;
1483 }
1484
1485 /* connection is now resume */
1486 ok = TRUE;
1487
1488 done :
1489
1490 pthread_mutex_unlock(&connectionPrivate->lock);
1491 return ok;
1492 }
1493
1494
1495 CFDictionaryRef
1496 SCNetworkConnectionCopyUserOptions(SCNetworkConnectionRef connection)
1497 {
1498 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1499 xmlDataOut_t data = NULL;
1500 mach_msg_type_number_t datalen = 0;
1501 int sc_status = kSCStatusFailed;
1502 mach_port_t session_port;
1503 kern_return_t status;
1504 CFPropertyListRef userOptions = NULL;
1505
1506 if (!isA_SCNetworkConnection(connection)) {
1507 _SCErrorSet(kSCStatusInvalidArgument);
1508 return NULL;
1509 }
1510
1511 pthread_mutex_lock(&connectionPrivate->lock);
1512
1513 retry :
1514
1515 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1516 if (session_port == MACH_PORT_NULL) {
1517 goto done;
1518 }
1519
1520 status = pppcontroller_copyuseroptions(session_port, &data, &datalen, &sc_status);
1521 if (__SCNetworkConnectionNeedsRetry(connection,
1522 "SCNetworkConnectionCopyUserOptions()",
1523 status,
1524 &sc_status)) {
1525 goto retry;
1526 }
1527
1528 if (data != NULL) {
1529 if (!_SCUnserialize(&userOptions, NULL, data, datalen)) {
1530 if (sc_status != kSCStatusOK) sc_status = SCError();
1531 }
1532 if ((sc_status == kSCStatusOK) && (userOptions != NULL) && !isA_CFDictionary(userOptions)) {
1533 sc_status = kSCStatusFailed;
1534 }
1535 }
1536
1537 if (sc_status == kSCStatusOK) {
1538 if (userOptions == NULL) {
1539 // if no user options, return an empty dictionary
1540 userOptions = CFDictionaryCreate(NULL,
1541 NULL,
1542 NULL,
1543 0,
1544 &kCFTypeDictionaryKeyCallBacks,
1545 &kCFTypeDictionaryValueCallBacks);
1546 }
1547 } else {
1548 if (userOptions) {
1549 CFRelease(userOptions);
1550 userOptions = NULL;
1551 }
1552 _SCErrorSet(sc_status);
1553 }
1554
1555 done :
1556
1557 pthread_mutex_unlock(&connectionPrivate->lock);
1558 return userOptions;
1559 }
1560
1561
1562 static Boolean
1563 __SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection,
1564 CFRunLoopRef runLoop,
1565 CFStringRef runLoopMode,
1566 dispatch_queue_t queue)
1567 {
1568 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1569 Boolean ok = FALSE;
1570 int sc_status = kSCStatusFailed;
1571 mach_port_t session_port;
1572 kern_return_t status;
1573
1574 pthread_mutex_lock(&connectionPrivate->lock);
1575
1576 if (connectionPrivate->rlsFunction == NULL) {
1577 _SCErrorSet(kSCStatusInvalidArgument);
1578 goto done;
1579 }
1580
1581 if ((connectionPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue
1582 ((queue != NULL) && connectionPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop
1583 _SCErrorSet(kSCStatusInvalidArgument);
1584 goto done;
1585 }
1586
1587 if (!connectionPrivate->scheduled) {
1588
1589 retry :
1590
1591 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1592 if (session_port == MACH_PORT_NULL) {
1593 goto done;
1594 }
1595
1596 status = pppcontroller_notification(session_port, 1, &sc_status);
1597 if (__SCNetworkConnectionNeedsRetry(connection,
1598 "__SCNetworkConnectionScheduleWithRunLoop()",
1599 status,
1600 &sc_status)) {
1601 goto retry;
1602 }
1603
1604 if (sc_status != kSCStatusOK) {
1605 _SCErrorSet(sc_status);
1606 goto done;
1607 }
1608
1609 if (runLoop != NULL) {
1610 connectionPrivate->rls = CFMachPortCreateRunLoopSource(NULL, connectionPrivate->notify_port, 0);
1611 connectionPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1612 }
1613
1614 connectionPrivate->scheduled = TRUE;
1615 }
1616
1617 if (queue != NULL) {
1618 mach_port_t mp;
1619 dispatch_source_t source;
1620
1621 mp = CFMachPortGetPort(connectionPrivate->notify_port);
1622 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue);
1623 if (source == NULL) {
1624 SCLog(TRUE, LOG_ERR, CFSTR("SCNetworkConnection dispatch_source_create() failed"));
1625 _SCErrorSet(kSCStatusFailed);
1626 goto done;
1627 }
1628
1629 // have our dispatch source hold a reference to the notification CFMachPort
1630 CFRetain(connectionPrivate->notify_port);
1631 dispatch_set_context(source, (void *)connectionPrivate->notify_port);
1632 dispatch_set_finalizer_f(source, (dispatch_function_t)CFRelease);
1633
1634 // retain the dispatch queue
1635 connectionPrivate->dispatchQueue = queue;
1636 dispatch_retain(connectionPrivate->dispatchQueue);
1637
1638 //
1639 // We've taken a reference to the callers dispatch_queue and we
1640 // want to hold on to that reference until we've processed any/all
1641 // notifications. To facilitate this we create a group, dispatch
1642 // any notification blocks to via that group, and when the caller
1643 // has told us to stop the notifications (unschedule) we wait for
1644 // the group to empty and use the group's finalizer to release
1645 // our reference to the SCNetworkConnection.
1646 //
1647 connectionPrivate->dispatchGroup = dispatch_group_create();
1648 CFRetain(connection);
1649 dispatch_set_context(connectionPrivate->dispatchGroup, (void *)connection);
1650 dispatch_set_finalizer_f(connectionPrivate->dispatchGroup, (dispatch_function_t)CFRelease);
1651
1652 dispatch_source_set_event_handler(source, ^{
1653 kern_return_t kr;
1654 union {
1655 u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE];
1656 mach_msg_empty_rcv_t msg;
1657 mach_no_senders_notification_t no_senders;
1658 } notify_msg;
1659 CFMachPortRef notify_port;
1660
1661 kr = mach_msg(&notify_msg.msg.header, // msg
1662 MACH_RCV_MSG, // options
1663 0, // send_size
1664 sizeof(notify_msg), // rcv_size
1665 mp, // rcv_name
1666 MACH_MSG_TIMEOUT_NONE, // timeout
1667 MACH_PORT_NULL); // notify
1668 if (kr != KERN_SUCCESS) {
1669 SCLog(TRUE, LOG_ERR,
1670 CFSTR("SCDynamicStore notification handler, kr=0x%x"),
1671 kr);
1672 return;
1673 }
1674
1675 CFRetain(connection);
1676 notify_port = dispatch_get_context(source);
1677 dispatch_group_async(connectionPrivate->dispatchGroup, connectionPrivate->dispatchQueue, ^{
1678 __SCNetworkConnectionCallBack(notify_port,
1679 (void *)&notify_msg.msg,
1680 sizeof(notify_msg),
1681 (void *)connection);
1682 CFRelease(connection);
1683 });
1684 });
1685
1686 dispatch_source_set_cancel_handler(source, ^{
1687 dispatch_release(source);
1688 });
1689
1690 connectionPrivate->dispatchSource = source;
1691 dispatch_resume(source);
1692 } else {
1693 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
1694 /*
1695 * if we do not already have notifications scheduled with
1696 * this runLoop / runLoopMode
1697 */
1698 CFRunLoopAddSource(runLoop, connectionPrivate->rls, runLoopMode);
1699 }
1700
1701 _SC_schedule(connection, runLoop, runLoopMode, connectionPrivate->rlList);
1702 }
1703
1704 ok = TRUE;
1705
1706 done :
1707
1708 pthread_mutex_unlock(&connectionPrivate->lock);
1709 return ok;
1710 }
1711
1712
1713 static Boolean
1714 __SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection,
1715 CFRunLoopRef runLoop,
1716 CFStringRef runLoopMode,
1717 dispatch_queue_t queue)
1718 {
1719 SCNetworkConnectionPrivateRef connectionPrivate = (SCNetworkConnectionPrivateRef)connection;
1720 dispatch_group_t drainGroup = NULL;
1721 dispatch_queue_t drainQueue = NULL;
1722 int sc_status = kSCStatusFailed;
1723 CFIndex n = 0;
1724 Boolean ok = FALSE;
1725 mach_port_t session_port;
1726 kern_return_t status;
1727
1728 // hold a reference while we unschedule
1729 CFRetain(connection);
1730
1731 pthread_mutex_lock(&connectionPrivate->lock);
1732
1733 if ((runLoop != NULL) && !connectionPrivate->scheduled) { // if we should be scheduled (but are not)
1734 _SCErrorSet(kSCStatusInvalidArgument);
1735 goto done;
1736 }
1737
1738 if (((runLoop == NULL) && (connectionPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not)
1739 ((runLoop != NULL) && (connectionPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue)
1740 _SCErrorSet(kSCStatusInvalidArgument);
1741 goto done;
1742 }
1743
1744 session_port = __SCNetworkConnectionSessionPort(connectionPrivate);
1745 if (session_port == MACH_PORT_NULL) {
1746 goto done;
1747 }
1748
1749 if (connectionPrivate->dispatchQueue != NULL) {
1750 // cancel dispatchSource
1751 if (connectionPrivate->dispatchSource != NULL) {
1752 dispatch_source_cancel(connectionPrivate->dispatchSource);
1753 connectionPrivate->dispatchSource = NULL;
1754 }
1755
1756 // save dispatchQueue/group, release reference when all queue'd blocks
1757 // have been processed, allow re-scheduling
1758 drainGroup = connectionPrivate->dispatchGroup;
1759 connectionPrivate->dispatchGroup = NULL;
1760 drainQueue = connectionPrivate->dispatchQueue;
1761 connectionPrivate->dispatchQueue = NULL;
1762 } else {
1763 if (!_SC_unschedule(connection, runLoop, runLoopMode, connectionPrivate->rlList, FALSE)) {
1764 // if not currently scheduled on this runLoop / runLoopMode
1765 _SCErrorSet(kSCStatusFailed);
1766 goto done;
1767 }
1768
1769 n = CFArrayGetCount(connectionPrivate->rlList);
1770 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, connectionPrivate->rlList)) {
1771 /*
1772 * if we are no longer scheduled to receive notifications for
1773 * this runLoop / runLoopMode
1774 */
1775 CFRunLoopRemoveSource(runLoop, connectionPrivate->rls, runLoopMode);
1776
1777 if (n == 0) {
1778 // if *all* notifications have been unscheduled
1779 CFRelease(connectionPrivate->rlList);
1780 connectionPrivate->rlList = NULL;
1781 CFRunLoopSourceInvalidate(connectionPrivate->rls);
1782 CFRelease(connectionPrivate->rls);
1783 connectionPrivate->rls = NULL;
1784 }
1785 }
1786 }
1787
1788 if (n == 0) {
1789 // if *all* notifications have been unscheduled
1790 connectionPrivate->scheduled = FALSE;
1791
1792 status = pppcontroller_notification(session_port, 0, &sc_status);
1793 if (__SCNetworkConnectionNeedsRetry(connection,
1794 "__SCNetworkConnectionUnscheduleFromRunLoop pppcontroller_notification()",
1795 status,
1796 &sc_status)) {
1797 sc_status = kSCStatusOK;
1798 status = KERN_SUCCESS;
1799 }
1800
1801 if ((status != KERN_SUCCESS) || (sc_status != kSCStatusOK)) {
1802 _SCErrorSet(sc_status);
1803 goto done;
1804 }
1805 }
1806
1807 ok = TRUE;
1808
1809 done :
1810
1811 pthread_mutex_unlock(&connectionPrivate->lock);
1812
1813 if (drainGroup != NULL) {
1814 dispatch_group_notify(drainGroup, drainQueue, ^{
1815 // release group/queue references
1816 dispatch_release(drainQueue);
1817 dispatch_release(drainGroup); // releases our connection reference
1818 });
1819 }
1820
1821 // release our reference
1822 CFRelease(connection);
1823
1824 return ok;
1825 }
1826
1827
1828 Boolean
1829 SCNetworkConnectionScheduleWithRunLoop(SCNetworkConnectionRef connection,
1830 CFRunLoopRef runLoop,
1831 CFStringRef runLoopMode)
1832 {
1833 if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) {
1834 _SCErrorSet(kSCStatusInvalidArgument);
1835 return FALSE;
1836 }
1837
1838 return __SCNetworkConnectionScheduleWithRunLoop(connection, runLoop, runLoopMode, NULL);
1839 }
1840
1841
1842 Boolean
1843 SCNetworkConnectionUnscheduleFromRunLoop(SCNetworkConnectionRef connection,
1844 CFRunLoopRef runLoop,
1845 CFStringRef runLoopMode)
1846 {
1847 if (!isA_SCNetworkConnection(connection) || (runLoop == NULL) || (runLoopMode == NULL)) {
1848 _SCErrorSet(kSCStatusInvalidArgument);
1849 return FALSE;
1850 }
1851
1852 return __SCNetworkConnectionUnscheduleFromRunLoop(connection, runLoop, runLoopMode, NULL);
1853 }
1854
1855
1856 Boolean
1857 SCNetworkConnectionSetDispatchQueue(SCNetworkConnectionRef connection,
1858 dispatch_queue_t queue)
1859 {
1860 Boolean ok = FALSE;
1861
1862 if (!isA_SCNetworkConnection(connection)) {
1863 _SCErrorSet(kSCStatusInvalidArgument);
1864 return FALSE;
1865 }
1866
1867 if (queue != NULL) {
1868 ok = __SCNetworkConnectionScheduleWithRunLoop(connection, NULL, NULL, queue);
1869 } else {
1870 ok = __SCNetworkConnectionUnscheduleFromRunLoop(connection, NULL, NULL, NULL);
1871 }
1872
1873 return ok;
1874 }
1875
1876
1877 #pragma mark -
1878 #pragma mark User level "dial" API
1879
1880
1881 #define k_NetworkConnect_Notification "com.apple.networkConnect"
1882 #define k_NetworkConnect_Pref_File CFSTR("com.apple.networkConnect")
1883 #define k_InterentConnect_Pref_File CFSTR("com.apple.internetconnect")
1884
1885 #define k_Dial_Default_Key CFSTR("ConnectByDefault") // needs to go into SC
1886 #define k_Last_Service_Id_Key CFSTR("ServiceID")
1887 #define k_Unique_Id_Key CFSTR("UniqueIdentifier")
1888
1889
1890 /* Private Prototypes */
1891 static Boolean SCNetworkConnectionPrivateCopyDefaultServiceIDForDial (SCDynamicStoreRef session, CFStringRef *serviceID);
1892 static Boolean SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore (SCDynamicStoreRef session, CFStringRef *serviceID);
1893 static Boolean SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions);
1894 static Boolean SCNetworkConnectionPrivateIsPPPService (SCDynamicStoreRef session, CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2);
1895 static void addPasswordFromKeychain (SCDynamicStoreRef session, CFStringRef serviceID, CFDictionaryRef *userOptions);
1896 static CFStringRef copyPasswordFromKeychain (CFStringRef uniqueID);
1897
1898 static int notify_userprefs_token = -1;
1899
1900 static CFDictionaryRef onDemand_configuration = NULL;
1901 static pthread_mutex_t onDemand_notify_lock = PTHREAD_MUTEX_INITIALIZER;
1902 static int onDemand_notify_token = -1;
1903
1904 /*
1905 * return TRUE if domain1 ends with domain2, and will check for trailing "."
1906 */
1907 Boolean
1908 _SC_domainEndsWithDomain(CFStringRef compare_domain, CFStringRef match_domain)
1909 {
1910 CFRange range;
1911 Boolean ret = FALSE;
1912 CFStringRef s1 = NULL;
1913 Boolean s1_created = FALSE;
1914 CFStringRef s2 = NULL;
1915 Boolean s2_created = FALSE;
1916 CFStringRef s3 = NULL;
1917
1918 if (CFStringHasSuffix(compare_domain, CFSTR("."))) {
1919 range.location = 0;
1920 range.length = CFStringGetLength(compare_domain) - 1;
1921 s1 = CFStringCreateWithSubstring(NULL, compare_domain, range);
1922 if (s1 == NULL) {
1923 goto done;
1924 }
1925 s1_created = TRUE;
1926 } else {
1927 s1 = compare_domain;
1928 }
1929
1930 if (CFStringHasSuffix(match_domain, CFSTR("."))) {
1931 range.location = 0;
1932 range.length = CFStringGetLength(match_domain) - 1;
1933 s2 = CFStringCreateWithSubstring(NULL, match_domain, range);
1934 if (s2 == NULL) {
1935 goto done;
1936 }
1937 s2_created = TRUE;
1938 } else {
1939 s2 = match_domain;
1940 }
1941
1942 if (CFStringHasPrefix(s2, CFSTR("*."))) {
1943 range.location = 2;
1944 range.length = CFStringGetLength(s2)-2;
1945 s3 = CFStringCreateWithSubstring(NULL, s2, range);
1946 if (s3 == NULL) {
1947 goto done;
1948 }
1949 if (s2_created) {
1950 CFRelease(s2);
1951 }
1952 s2 = s3;
1953 s2_created = TRUE;
1954 }
1955
1956 ret = CFStringHasSuffix(s1, s2);
1957
1958 done :
1959
1960 if (s1_created) CFRelease(s1);
1961 if (s2_created) CFRelease(s2);
1962 return ret;
1963 }
1964
1965 /* VPN On Demand */
1966
1967 Boolean
1968 __SCNetworkConnectionCopyOnDemandInfoWithName(SCDynamicStoreRef *storeP,
1969 CFStringRef hostName,
1970 Boolean onDemandRetry,
1971 CFStringRef *connectionServiceID,
1972 SCNetworkConnectionStatus *connectionStatus,
1973 CFStringRef *vpnRemoteAddress) /* CFDictionaryRef *info */
1974 {
1975 int changed = 1;
1976 CFDictionaryRef configuration;
1977 Boolean ok = FALSE;
1978 int status;
1979 SCDynamicStoreRef store = *storeP;
1980 CFArrayRef triggers;
1981 uint64_t triggersCount = 0;
1982 int triggersIndex;
1983
1984 pthread_mutex_lock(&onDemand_notify_lock);
1985 if (onDemand_notify_token == -1) {
1986 status = notify_register_check(kSCNETWORKCONNECTION_ONDEMAND_NOTIFY_KEY, &onDemand_notify_token);
1987 if (status != NOTIFY_STATUS_OK) {
1988 SCLog(TRUE, LOG_ERR, CFSTR("notify_register_check() failed, status=%lu"), status);
1989 onDemand_notify_token = -1;
1990 }
1991 }
1992 if (onDemand_notify_token != -1) {
1993 status = notify_check(onDemand_notify_token, &changed);
1994 if (status != NOTIFY_STATUS_OK) {
1995 SCLog(TRUE, LOG_ERR, CFSTR("notify_check() failed, status=%lu"), status);
1996 (void)notify_cancel(onDemand_notify_token);
1997 onDemand_notify_token = -1;
1998 }
1999 }
2000
2001 if (changed && (onDemand_notify_token != -1)) {
2002 status = notify_get_state(onDemand_notify_token, &triggersCount);
2003 if (status != NOTIFY_STATUS_OK) {
2004 SCLog(TRUE, LOG_ERR, CFSTR("notify_get_state() failed, status=%lu"), status);
2005 (void)notify_cancel(onDemand_notify_token);
2006 onDemand_notify_token = -1;
2007 }
2008 }
2009
2010 if (changed) {
2011 CFStringRef key;
2012
2013 if (_sc_debug || (debug > 0)) {
2014 SCLog(TRUE, LOG_INFO,
2015 CFSTR("OnDemand information %s"),
2016 (onDemand_configuration == NULL) ? "fetched" : "updated");
2017 }
2018
2019 if (onDemand_configuration != NULL) {
2020 CFRelease(onDemand_configuration);
2021 onDemand_configuration = NULL;
2022 }
2023
2024 if (triggersCount > 0) {
2025 if (store == NULL) {
2026 store = SCDynamicStoreCreate(NULL, CFSTR("__SCNetworkConnectionCopyOnDemandInfoWithName"), NULL, NULL);
2027 if (store == NULL) {
2028 SCLog(TRUE, LOG_ERR, CFSTR("__SCNetworkConnectionCopyOnDemandInfoWithName SCDynamicStoreCreate() failed"));
2029
2030 // force retry on next check
2031 if (onDemand_notify_token != -1) {
2032 (void)notify_cancel(onDemand_notify_token);
2033 onDemand_notify_token = -1;
2034 }
2035 pthread_mutex_unlock(&onDemand_notify_lock);
2036 return FALSE;
2037 }
2038 *storeP = store;
2039 }
2040
2041 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetOnDemand);
2042 onDemand_configuration = SCDynamicStoreCopyValue(store, key);
2043 CFRelease(key);
2044 if ((onDemand_configuration != NULL) && !isA_CFDictionary(onDemand_configuration)) {
2045 CFRelease(onDemand_configuration);
2046 onDemand_configuration = NULL;
2047 }
2048 }
2049 }
2050
2051 configuration = (onDemand_configuration != NULL) ? CFRetain(onDemand_configuration) : NULL;
2052 pthread_mutex_unlock(&onDemand_notify_lock);
2053
2054 if (configuration == NULL) {
2055 // if no "OnDemand" configurations
2056 return FALSE;
2057 }
2058
2059 triggers = CFDictionaryGetValue(configuration, kSCNetworkConnectionOnDemandTriggers);
2060 triggersCount = isA_CFArray(triggers) ? CFArrayGetCount(triggers) : 0;
2061 for (triggersIndex = 0; triggersIndex < triggersCount; triggersIndex++) {
2062 CFArrayRef domains;
2063 int domainsCount;
2064 int domainsIndex;
2065 CFStringRef key;
2066 CFDictionaryRef trigger;
2067
2068 trigger = CFArrayGetValueAtIndex(triggers, triggersIndex);
2069 if (!isA_CFDictionary(trigger)) {
2070 // if not a valid "OnDemand" configuration
2071 continue;
2072 }
2073
2074 /*
2075 * If we haven't tried a resulution yet, we only want to check for a name
2076 * match for domains that require to always connect.
2077 */
2078 key = onDemandRetry ? kSCNetworkConnectionOnDemandMatchDomainsOnRetry
2079 : kSCNetworkConnectionOnDemandMatchDomainsAlways;
2080 domains = CFDictionaryGetValue(trigger, key);
2081 domainsCount = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
2082 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
2083 CFStringRef domain;
2084
2085 domain = CFArrayGetValueAtIndex(domains, domainsIndex);
2086 if (!isA_CFString(domain)) {
2087 // if not a valid match domain
2088 continue;
2089 }
2090
2091 if (_SC_domainEndsWithDomain(hostName, domain)) {
2092 CFArrayRef exceptions;
2093 int exceptionsCount;
2094 int exceptionsIndex;
2095 CFNumberRef num;
2096 SCNetworkConnectionStatus onDemandStatus = kSCNetworkConnectionDisconnected;
2097
2098 // we have a matching domain, check against exception list
2099 exceptions = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandMatchDomainsNever);
2100 exceptionsCount = isA_CFArray(exceptions) ? CFArrayGetCount(exceptions) : 0;
2101 for (exceptionsIndex = 0; exceptionsIndex < exceptionsCount; exceptionsIndex++) {
2102 CFStringRef exception;
2103
2104 exception = CFArrayGetValueAtIndex(exceptions, exceptionsIndex);
2105 if (!isA_CFString(exception)) {
2106 // if not a valid match exception
2107 continue;
2108 }
2109
2110 if (_SC_domainEndsWithDomain(hostName, exception)) {
2111 // found matching exception
2112 if (_sc_debug || (debug > 0)) {
2113 SCLog(TRUE, LOG_INFO, CFSTR("OnDemand match exception"));
2114 }
2115 goto done;
2116 }
2117 }
2118
2119 // if we have a matching domain and there were no exceptions
2120 // then we pass back the OnDemand info
2121
2122 ok = TRUE;
2123
2124 if (!CFDictionaryGetValueIfPresent(trigger,
2125 kSCNetworkConnectionOnDemandStatus,
2126 (const void **)&num) ||
2127 !isA_CFNumber(num) ||
2128 !CFNumberGetValue(num, kCFNumberSInt32Type, &onDemandStatus)) {
2129 onDemandStatus = kSCNetworkConnectionDisconnected;
2130 }
2131 if (connectionStatus != NULL) {
2132 *connectionStatus = onDemandStatus;
2133 }
2134
2135 if (connectionServiceID != NULL) {
2136 *connectionServiceID = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandServiceID);
2137 *connectionServiceID = isA_CFString(*connectionServiceID);
2138 if ((*connectionServiceID != NULL) && (CFStringGetLength(*connectionServiceID) > 0)) {
2139 CFRetain(*connectionServiceID);
2140 } else {
2141 SCLog(TRUE, LOG_INFO,
2142 CFSTR("OnDemand%s configuration error, no serviceID"),
2143 onDemandRetry ? " (on retry)" : "");
2144
2145 *connectionServiceID = NULL;
2146 ok = FALSE;
2147 }
2148 }
2149
2150 if (vpnRemoteAddress != NULL) {
2151 *vpnRemoteAddress = CFDictionaryGetValue(trigger, kSCNetworkConnectionOnDemandRemoteAddress);
2152 *vpnRemoteAddress = isA_CFString(*vpnRemoteAddress);
2153 if ((*vpnRemoteAddress != NULL) && (CFStringGetLength(*vpnRemoteAddress) > 0)) {
2154 CFRetain(*vpnRemoteAddress);
2155 } else {
2156 SCLog(TRUE, LOG_INFO,
2157 CFSTR("OnDemand%s configuration error, no server address"),
2158 onDemandRetry ? " (on retry)" : "");
2159
2160 *vpnRemoteAddress = NULL;
2161 ok = FALSE;
2162 }
2163 }
2164
2165 if (!ok) {
2166 if ((connectionServiceID != NULL) && (*connectionServiceID != NULL)) {
2167 CFRelease(*connectionServiceID);
2168 *connectionServiceID = NULL;
2169 }
2170 if ((vpnRemoteAddress != NULL) && (*vpnRemoteAddress != NULL)) {
2171 CFRelease(*vpnRemoteAddress);
2172 *vpnRemoteAddress = NULL;
2173 }
2174 continue;
2175 }
2176
2177 if (_sc_debug || (debug > 0)) {
2178 SCLog(TRUE, LOG_INFO,
2179 CFSTR("OnDemand%s match, connection status = %d"),
2180 onDemandRetry ? " (on retry)" : "",
2181 onDemandStatus);
2182 }
2183
2184 goto done;
2185 }
2186 }
2187 }
2188
2189 // if (_sc_debug || (debug > 0)) {
2190 // SCLog(TRUE, LOG_INFO, CFSTR("OnDemand domain name(s) not matched"));
2191 // }
2192
2193 done :
2194
2195 if (configuration != NULL) CFRelease(configuration);
2196 return ok;
2197 }
2198
2199
2200 Boolean
2201 SCNetworkConnectionCopyUserPreferences(CFDictionaryRef selectionOptions,
2202 CFStringRef *serviceID,
2203 CFDictionaryRef *userOptions)
2204 {
2205 int prefsChanged = 1;
2206 SCDynamicStoreRef session = NULL;
2207 Boolean success = FALSE;
2208 int status;
2209
2210
2211 /* initialize runtime */
2212 pthread_once(&initialized, __SCNetworkConnectionInitialize);
2213
2214 /* first check for new VPN OnDemand style */
2215 if (selectionOptions != NULL) {
2216 CFStringRef hostName;
2217
2218 hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
2219 if (isA_CFString(hostName)) {
2220 CFStringRef connectionServiceID = NULL;
2221 SCNetworkConnectionStatus connectionStatus;
2222 Boolean onDemandRetry;
2223 CFTypeRef val;
2224
2225 val = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandRetry);
2226 onDemandRetry = isA_CFBoolean(val) ? CFBooleanGetValue(val) : TRUE;
2227
2228 success = __SCNetworkConnectionCopyOnDemandInfoWithName(&session,
2229 hostName,
2230 onDemandRetry,
2231 &connectionServiceID,
2232 &connectionStatus,
2233 NULL);
2234 if (debug > 1) {
2235 SCLog(TRUE, LOG_DEBUG,
2236 CFSTR("SCNetworkConnectionCopyUserPreferences __SCNetworkConnectionCopyOnDemandInfoWithName returns %d w/status %d"),
2237 success,
2238 connectionStatus);
2239 }
2240
2241 if (success) {
2242 // if the hostname matches an OnDemand domain
2243 if (session != NULL) {
2244 CFRelease(session);
2245 }
2246 if (connectionStatus == kSCNetworkConnectionConnected) {
2247 // if we are already connected
2248 if (connectionServiceID != NULL) {
2249 CFRelease(connectionServiceID);
2250 }
2251 return FALSE;
2252 }
2253
2254 *serviceID = connectionServiceID;
2255 *userOptions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2256 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
2257 return TRUE;
2258 } else if (!onDemandRetry) {
2259 // if the hostname does not match an OnDemand domain and we have
2260 // not yet issued an initial DNS query (i.e. it's not a query
2261 // being retried after the VPN has been established) than we're
2262 // done
2263 if (session != NULL) {
2264 CFRelease(session);
2265 }
2266 return FALSE;
2267 }
2268 }
2269 }
2270
2271 if (notify_userprefs_token == -1) {
2272 status = notify_register_check(k_NetworkConnect_Notification, &notify_userprefs_token);
2273 if (status != NOTIFY_STATUS_OK) {
2274 SCLog(TRUE, LOG_ERR, CFSTR("notify_register_check() failed, status=%lu"), status);
2275 (void)notify_cancel(notify_userprefs_token);
2276 notify_userprefs_token = -1;
2277 } else {
2278 // clear the "something has changed" state
2279 (void) notify_check(notify_userprefs_token, &prefsChanged);
2280 prefsChanged = 1;
2281 }
2282 }
2283 if (notify_userprefs_token != -1) {
2284 status = notify_check(notify_userprefs_token, &prefsChanged);
2285 if (status != NOTIFY_STATUS_OK) {
2286 SCLog(TRUE, LOG_ERR, CFSTR("notify_check() failed, status=%lu"), status);
2287 (void)notify_cancel(notify_userprefs_token);
2288 notify_userprefs_token = -1;
2289 }
2290 }
2291
2292
2293 *serviceID = NULL;
2294 *userOptions = NULL;
2295
2296 if (session == NULL) {
2297 session = SCDynamicStoreCreate(NULL, CFSTR("SCNetworkConnection"), NULL, NULL);
2298 }
2299 if (session == NULL) {
2300 SCLog(TRUE, LOG_ERR, CFSTR("Error, SCNetworkConnectionCopyUserPreferences, SCDynamicStoreCreate() returned NULL!"));
2301 return FALSE;
2302 }
2303
2304 if (selectionOptions != NULL) {
2305 Boolean catchAllFound = FALSE;
2306 CFIndex catchAllService = 0;
2307 CFIndex catchAllConfig = 0;
2308 CFStringRef hostName = NULL;
2309 CFStringRef priority = NULL;
2310 CFArrayRef serviceNames = NULL;
2311 CFDictionaryRef services = NULL;
2312 CFIndex serviceIndex;
2313 CFIndex servicesCount;
2314
2315 hostName = CFDictionaryGetValue(selectionOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName);
2316 if (hostName == NULL) {
2317 hostName = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandHostName);
2318 }
2319 hostName = isA_CFString(hostName);
2320 if (hostName == NULL)
2321 goto done_selection; // if no hostname for matching
2322
2323 priority = CFDictionaryGetValue(selectionOptions, kSCPropNetPPPOnDemandPriority);
2324 if (!isA_CFString(priority))
2325 priority = kSCValNetPPPOnDemandPriorityDefault;
2326
2327
2328 if (!isA_CFArray(serviceNames))
2329 goto done_selection;
2330
2331
2332 if (!isA_CFDictionary(services))
2333 goto done_selection;
2334
2335 servicesCount = CFArrayGetCount(serviceNames);
2336 for (serviceIndex = 0; serviceIndex < servicesCount; serviceIndex++) {
2337 CFIndex configIndex;
2338 CFIndex configsCount;
2339 CFArrayRef serviceConfigs;
2340 CFStringRef serviceName;
2341 int val;
2342
2343 serviceName = CFArrayGetValueAtIndex(serviceNames, serviceIndex);
2344 if (!isA_CFString(serviceName))
2345 continue;
2346
2347 serviceConfigs = CFDictionaryGetValue(services, serviceName);
2348 if (!isA_CFArray(serviceConfigs))
2349 continue;
2350
2351 configsCount = CFArrayGetCount(serviceConfigs);
2352 for (configIndex = 0; configIndex < configsCount; configIndex++) {
2353 CFNumberRef autodial;
2354 CFDictionaryRef config;
2355 CFDictionaryRef pppConfig;
2356
2357 config = CFArrayGetValueAtIndex(serviceConfigs, configIndex);
2358 if (!isA_CFDictionary(config))
2359 continue;
2360
2361 pppConfig = CFDictionaryGetValue(config, kSCEntNetPPP);
2362 if (!isA_CFDictionary(pppConfig))
2363 continue;
2364
2365 autodial = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandEnabled);
2366 if (!isA_CFNumber(autodial))
2367 continue;
2368
2369 CFNumberGetValue(autodial, kCFNumberIntType, &val);
2370 if (val) {
2371 CFArrayRef domains;
2372 CFIndex domainsCount;
2373 CFIndex domainsIndex;
2374
2375 /* we found an conditional connection enabled configuration */
2376
2377 /* check domain */
2378 domains = CFDictionaryGetValue(pppConfig, kSCPropNetPPPOnDemandDomains);
2379 if (!isA_CFArray(domains))
2380 continue;
2381
2382 domainsCount = CFArrayGetCount(domains);
2383 for (domainsIndex = 0; domainsIndex < domainsCount; domainsIndex++) {
2384 CFStringRef domain;
2385
2386 domain = CFArrayGetValueAtIndex(domains, domainsIndex);
2387 if (!isA_CFString(domain))
2388 continue;
2389
2390 if (!catchAllFound &&
2391 (CFStringCompare(domain, CFSTR(""), 0) == kCFCompareEqualTo
2392 || CFStringCompare(domain, CFSTR("."), 0) == kCFCompareEqualTo)) {
2393 // found a catch all
2394 catchAllFound = TRUE;
2395 catchAllService = serviceIndex;
2396 catchAllConfig = configIndex;
2397 }
2398
2399 if (_SC_domainEndsWithDomain(hostName, domain)) {
2400 // found matching configuration
2401 *serviceID = serviceName;
2402 CFRetain(*serviceID);
2403 *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config);
2404 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
2405 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority);
2406 addPasswordFromKeychain(session, *serviceID, userOptions);
2407 success = TRUE;
2408 goto done_selection;
2409 }
2410 }
2411 }
2412 }
2413 }
2414
2415 // config not found, do we have a catchall ?
2416 if (catchAllFound) {
2417 CFDictionaryRef config;
2418 CFArrayRef serviceConfigs;
2419 CFStringRef serviceName;
2420
2421 serviceName = CFArrayGetValueAtIndex(serviceNames, catchAllService);
2422 serviceConfigs = CFDictionaryGetValue(services, serviceName);
2423 config = CFArrayGetValueAtIndex(serviceConfigs, catchAllConfig);
2424
2425 *serviceID = serviceName;
2426 CFRetain(*serviceID);
2427 *userOptions = CFDictionaryCreateMutableCopy(NULL, 0, config);
2428 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCNetworkConnectionSelectionOptionOnDemandHostName, hostName);
2429 CFDictionarySetValue((CFMutableDictionaryRef)*userOptions, kSCPropNetPPPOnDemandPriority, priority);
2430 addPasswordFromKeychain(session, *serviceID, userOptions);
2431 success = TRUE;
2432 goto done_selection;
2433 }
2434
2435 done_selection:
2436
2437 if (serviceNames)
2438 CFRelease(serviceNames);
2439 if (services)
2440 CFRelease(services);
2441 CFRelease(session);
2442
2443 if (debug > 1) {
2444 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionCopyUserPreferences %@"), success ? CFSTR("succeeded") : CFSTR("failed"));
2445 SCLog(TRUE, LOG_DEBUG, CFSTR("Selection options: %@"), selectionOptions);
2446 }
2447
2448 return success;
2449 }
2450
2451 /* we don't have selection options */
2452
2453 // (1) Figure out which service ID we care about, allocate it into passed "serviceID"
2454 success = SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(session, serviceID);
2455
2456 if (success && (*serviceID != NULL)) {
2457 // (2) Get the list of user data for this service ID
2458 CFPropertyListRef userServices = NULL;
2459
2460
2461 // (3) We are expecting an array if the user has defined records for this service ID or NULL if the user hasn't
2462 if (userServices != NULL) {
2463 if (isA_CFArray(userServices)) {
2464 // (4) Get the default set of user options for this service
2465 success = SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray((CFArrayRef)userServices,
2466 userOptions);
2467 if(success && (userOptions != NULL)) {
2468 addPasswordFromKeychain(session, *serviceID, userOptions);
2469 }
2470 } else {
2471 SCLog(TRUE, LOG_DEBUG, CFSTR("Error, userServices are not of type CFArray!"));
2472 }
2473
2474 CFRelease(userServices); // this is OK because SCNetworkConnectionPrivateISExpectedCFType() checks for NULL
2475 }
2476 }
2477
2478 if (debug > 1) {
2479 SCLog(TRUE, LOG_DEBUG, CFSTR("SCNetworkConnectionCopyUserPreferences %@, no selection options"), success ? CFSTR("succeeded") : CFSTR("failed"));
2480 }
2481
2482 CFRelease(session);
2483 return success;
2484 }
2485
2486
2487 //*******************************************************************************************
2488 // SCNetworkConnectionPrivateCopyDefaultServiceIDForDial
2489 // ----------------------------------------------------
2490 // Try to find the service id to connect
2491 // (1) Start by looking at the last service used in Network Pref / Network menu extra
2492 // (2) If Network Pref / Network menu extra has not been used, find the PPP service
2493 // with the highest ordering
2494 //********************************************************************************************
2495 static Boolean
2496 SCNetworkConnectionPrivateCopyDefaultServiceIDForDial(SCDynamicStoreRef session, CFStringRef *serviceID)
2497 {
2498 Boolean foundService = FALSE;
2499 CFPropertyListRef lastServiceSelectedInIC = NULL;
2500
2501
2502
2503 // we found the service the user last had open in IC
2504 if (lastServiceSelectedInIC != NULL) {
2505 // make sure its a PPP service
2506 if (SCNetworkConnectionPrivateIsPPPService(session, lastServiceSelectedInIC, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) {
2507 // make sure the service that we found is valid
2508 CFDictionaryRef dict;
2509 CFStringRef key;
2510
2511 key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2512 kSCDynamicStoreDomainSetup,
2513 lastServiceSelectedInIC,
2514 kSCEntNetInterface);
2515 dict = SCDynamicStoreCopyValue(session, key);
2516 CFRelease(key);
2517 if (dict != NULL) {
2518 CFRelease(dict);
2519 *serviceID = CFRetain(lastServiceSelectedInIC);
2520 foundService = TRUE;
2521 }
2522 }
2523 CFRelease(lastServiceSelectedInIC);
2524 }
2525
2526 if (!foundService) {
2527 foundService = SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(session, serviceID);
2528 }
2529
2530 return foundService;
2531 }
2532
2533 //********************************************************************************
2534 // SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore
2535 // -------------------------------------------------------
2536 // Find the highest ordered PPP service in the dynamic store
2537 //********************************************************************************
2538 static Boolean
2539 SCNetworkConnectionPrivateGetPPPServiceFromDynamicStore(SCDynamicStoreRef session, CFStringRef *serviceID)
2540 {
2541 CFDictionaryRef dict = NULL;
2542 CFStringRef key = NULL;
2543 CFArrayRef serviceIDs = NULL;
2544 Boolean success = FALSE;
2545
2546 *serviceID = NULL;
2547
2548 do {
2549 CFIndex count;
2550 CFIndex i;
2551
2552 key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainSetup, kSCEntNetIPv4);
2553 if (key == NULL) {
2554 fprintf(stderr, "Error, Setup Key == NULL!\n");
2555 break;
2556 }
2557
2558 dict = SCDynamicStoreCopyValue(session, key);
2559 if (!isA_CFDictionary(dict)) {
2560 fprintf(stderr, "no global IPv4 entity\n");
2561 break;
2562 }
2563
2564 serviceIDs = CFDictionaryGetValue(dict, kSCPropNetServiceOrder); // array of service id's
2565 if (!isA_CFArray(serviceIDs)) {
2566 fprintf(stderr, "service order not specified\n");
2567 break;
2568 }
2569
2570 count = CFArrayGetCount(serviceIDs);
2571 for (i = 0; i < count; i++) {
2572 CFStringRef service = CFArrayGetValueAtIndex(serviceIDs, i);
2573
2574 if (SCNetworkConnectionPrivateIsPPPService(session, service, kSCValNetInterfaceSubTypePPPSerial, kSCValNetInterfaceSubTypePPPoE)) {
2575 *serviceID = CFRetain(service);
2576 success = TRUE;
2577 break;
2578 }
2579 }
2580 } while (FALSE);
2581
2582 if (key != NULL) CFRelease(key);
2583 if (dict != NULL) CFRelease(dict);
2584
2585 return success;
2586 }
2587
2588 //********************************************************************************
2589 // SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray
2590 // ---------------------------------------------------------
2591 // Copy over user preferences for a particular service if they exist
2592 //********************************************************************************
2593 static Boolean
2594 SCNetworkConnectionPrivateCopyDefaultUserOptionsFromArray(CFArrayRef userOptionsArray, CFDictionaryRef *userOptions)
2595 {
2596 CFIndex count = CFArrayGetCount(userOptionsArray);
2597 int i;
2598
2599 for (i = 0; i < count; i++) {
2600 // (1) Find the dictionary
2601 CFPropertyListRef propertyList = CFArrayGetValueAtIndex(userOptionsArray, i);
2602
2603 if (isA_CFDictionary(propertyList) != NULL) {
2604 // See if there's a value for dial on demand
2605 CFPropertyListRef value;
2606
2607 value = CFDictionaryGetValue((CFDictionaryRef)propertyList, k_Dial_Default_Key);
2608 if (isA_CFBoolean(value) != NULL) {
2609 if (CFBooleanGetValue(value)) {
2610 // we found the default user options
2611 *userOptions = CFDictionaryCreateCopy(NULL,
2612 (CFDictionaryRef)propertyList);
2613 break;
2614 }
2615 }
2616 }
2617 }
2618
2619 return TRUE;
2620 }
2621
2622 //********************************************************************************
2623 // SCNetworkConnectionPrivateIsServiceType
2624 // --------------------------------------
2625 // Check and see if the service is a PPP service of the given types
2626 //********************************************************************************
2627 static Boolean
2628 SCNetworkConnectionPrivateIsPPPService(SCDynamicStoreRef session, CFStringRef serviceID, CFStringRef subType1, CFStringRef subType2)
2629 {
2630 CFStringRef entityKey;
2631 Boolean isPPPService = FALSE;
2632 Boolean isMatchingSubType = FALSE;
2633 CFDictionaryRef serviceDict;
2634
2635 entityKey = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2636 kSCDynamicStoreDomainSetup,
2637 serviceID,
2638 kSCEntNetInterface);
2639 if (entityKey == NULL) {
2640 return FALSE;
2641 }
2642
2643 serviceDict = SCDynamicStoreCopyValue(session, entityKey);
2644 if (serviceDict != NULL) {
2645 if (isA_CFDictionary(serviceDict)) {
2646 CFStringRef type;
2647 CFStringRef subtype;
2648
2649 type = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceType);
2650 if (isA_CFString(type)) {
2651 isPPPService = CFEqual(type, kSCValNetInterfaceTypePPP);
2652 }
2653
2654 subtype = CFDictionaryGetValue(serviceDict, kSCPropNetInterfaceSubType);
2655 if (isA_CFString(subtype)) {
2656 isMatchingSubType = CFEqual(subtype, subType1);
2657 if (!isMatchingSubType && subType2)
2658 isMatchingSubType = CFEqual(subtype, subType2);
2659 }
2660 }
2661 CFRelease(serviceDict);
2662 }
2663 CFRelease(entityKey);
2664
2665 return (isPPPService && isMatchingSubType);
2666 }
2667
2668 //********************************************************************************
2669 // addPasswordFromKeychain
2670 // --------------------------------------
2671 // Get the password and shared secret out of the keychain and add
2672 // them to the PPP and IPSec dictionaries
2673 //********************************************************************************
2674 static void
2675 addPasswordFromKeychain(SCDynamicStoreRef session, CFStringRef serviceID, CFDictionaryRef *userOptions)
2676 {
2677 CFPropertyListRef uniqueID;
2678 CFStringRef password;
2679 CFStringRef sharedsecret = NULL;
2680
2681 /* user options must exist */
2682 if (*userOptions == NULL)
2683 return;
2684
2685 /* first, get the unique identifier used to store passwords in the keychain */
2686 uniqueID = CFDictionaryGetValue(*userOptions, k_Unique_Id_Key);
2687 if (!isA_CFString(uniqueID))
2688 return;
2689
2690 /* first, get the PPP password */
2691 password = copyPasswordFromKeychain(uniqueID);
2692
2693 /* then, if necessary, get the IPSec Shared Secret */
2694 if (SCNetworkConnectionPrivateIsPPPService(session, serviceID, kSCValNetInterfaceSubTypeL2TP, 0)) {
2695 CFMutableStringRef uniqueIDSS;
2696
2697 uniqueIDSS = CFStringCreateMutableCopy(NULL, 0, uniqueID);
2698 CFStringAppend(uniqueIDSS, CFSTR(".SS"));
2699 sharedsecret = copyPasswordFromKeychain(uniqueIDSS);
2700 CFRelease(uniqueIDSS);
2701 }
2702
2703 /* did we find our information in the key chain ? */
2704 if ((password != NULL) || (sharedsecret != NULL)) {
2705 CFMutableDictionaryRef newOptions;
2706
2707 newOptions = CFDictionaryCreateMutableCopy(NULL, 0, *userOptions);
2708
2709 /* PPP password */
2710 if (password != NULL) {
2711 CFDictionaryRef entity;
2712 CFMutableDictionaryRef newEntity;
2713
2714 entity = CFDictionaryGetValue(*userOptions, kSCEntNetPPP);
2715 if (isA_CFDictionary(entity))
2716 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
2717 else
2718 newEntity = CFDictionaryCreateMutable(NULL,
2719 0,
2720 &kCFTypeDictionaryKeyCallBacks,
2721 &kCFTypeDictionaryValueCallBacks);
2722
2723
2724 /* set the PPP password */
2725 CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPassword, uniqueID);
2726 CFDictionarySetValue(newEntity, kSCPropNetPPPAuthPasswordEncryption, kSCValNetPPPAuthPasswordEncryptionKeychain);
2727 CFRelease(password);
2728
2729 /* update the PPP entity */
2730 CFDictionarySetValue(newOptions, kSCEntNetPPP, newEntity);
2731 CFRelease(newEntity);
2732 }
2733
2734 /* IPSec Shared Secret */
2735 if (sharedsecret != NULL) {
2736 CFDictionaryRef entity;
2737 CFMutableDictionaryRef newEntity;
2738
2739 entity = CFDictionaryGetValue(*userOptions, kSCEntNetIPSec);
2740 if (isA_CFDictionary(entity))
2741 newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity);
2742 else
2743 newEntity = CFDictionaryCreateMutable(NULL,
2744 0,
2745 &kCFTypeDictionaryKeyCallBacks,
2746 &kCFTypeDictionaryValueCallBacks);
2747
2748 /* set the IPSec Shared Secret */
2749 CFDictionarySetValue(newEntity, kSCPropNetIPSecSharedSecret, sharedsecret);
2750 CFRelease(sharedsecret);
2751
2752 /* update the IPSec entity */
2753 CFDictionarySetValue(newOptions, kSCEntNetIPSec, newEntity);
2754 CFRelease(newEntity);
2755 }
2756
2757 /* update the userOptions dictionary */
2758 CFRelease(*userOptions);
2759 *userOptions = CFDictionaryCreateCopy(NULL, newOptions);
2760 CFRelease(newOptions);
2761 }
2762
2763 }
2764
2765 #if !TARGET_OS_IPHONE
2766 //********************************************************************************
2767 // copyKeychainEnumerator
2768 // --------------------------------------
2769 // Gather Keychain Enumerator
2770 //********************************************************************************
2771 static CFArrayRef
2772 copyKeychainEnumerator(CFStringRef uniqueIdentifier)
2773 {
2774 CFArrayRef itemArray = NULL;
2775 CFMutableDictionaryRef query;
2776 OSStatus result;
2777
2778 query = CFDictionaryCreateMutable(NULL,
2779 0,
2780 &kCFTypeDictionaryKeyCallBacks,
2781 &kCFTypeDictionaryValueCallBacks);
2782 CFDictionarySetValue(query, kSecClass , kSecClassGenericPassword);
2783 CFDictionarySetValue(query, kSecAttrService, uniqueIdentifier);
2784 CFDictionarySetValue(query, kSecReturnRef , kCFBooleanTrue);
2785 CFDictionarySetValue(query, kSecMatchLimit , kSecMatchLimitAll);
2786 result = SecItemCopyMatching(query, (CFTypeRef *)&itemArray);
2787 CFRelease(query);
2788 if ((result != noErr) && (itemArray != NULL)) {
2789 CFRelease(itemArray);
2790 itemArray = NULL;
2791 }
2792
2793 return itemArray;
2794 }
2795 #endif // !TARGET_OS_IPHONE
2796
2797 //********************************************************************************
2798 // copyPasswordFromKeychain
2799 // --------------------------------------
2800 // Given a uniqueID, retrieve the password from the keychain
2801 //********************************************************************************
2802 static CFStringRef
2803 copyPasswordFromKeychain(CFStringRef uniqueID)
2804 {
2805 #if !TARGET_OS_IPHONE
2806 CFArrayRef enumerator;
2807 CFIndex n;
2808 CFStringRef password = NULL;
2809
2810 enumerator = copyKeychainEnumerator(uniqueID);
2811 if (enumerator == NULL) {
2812 return NULL; // if no keychain enumerator
2813 }
2814
2815 n = CFArrayGetCount(enumerator);
2816 if (n > 0) {
2817 void *data = NULL;
2818 UInt32 dataLen = 0;
2819 SecKeychainItemRef itemRef;
2820 OSStatus result;
2821
2822 itemRef = (SecKeychainItemRef)CFArrayGetValueAtIndex(enumerator, 0);
2823 result = SecKeychainItemCopyContent(itemRef, // itemRef
2824 NULL, // itemClass
2825 NULL, // attrList
2826 &dataLen, // length
2827 (void *)&data); // outData
2828 if ((result == noErr) && (data != NULL) && (dataLen > 0)) {
2829 password = CFStringCreateWithBytes(NULL, data, dataLen, kCFStringEncodingUTF8, TRUE);
2830 (void) SecKeychainItemFreeContent(NULL, data);
2831 }
2832
2833 }
2834
2835 CFRelease(enumerator);
2836
2837 return password;
2838 #else // !TARGET_OS_IPHONE
2839 return NULL;
2840 #endif // !TARGET_OS_IPHONE
2841 }