1/*
2 * Copyright (c) 1998-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#include "DASession.h"
25
26#include "DADisk.h"
27#include "DAInternal.h"
28#include "DAServer.h"
29
30#include <bootstrap_priv.h>
31#include <crt_externs.h>
32#include <libgen.h>
33#include <pthread.h>
34#include <unistd.h>
35#include <dispatch/dispatch.h>
36#include <mach/mach.h>
37#include <mach-o/dyld.h>
38#include <servers/bootstrap.h>
39#include <CoreFoundation/CoreFoundation.h>
40#include <CoreFoundation/CFRuntime.h>
41#include <Security/Authorization.h>
42
43struct __DASession
44{
45    CFRuntimeBase      _base;
46
47    AuthorizationRef   _authorization;
48    CFMachPortRef      _client;
49    char *             _name;
50    pid_t              _pid;
51    mach_port_t        _server;
52    CFRunLoopSourceRef _source;
53    dispatch_source_t  _source2;
54    UInt32             _sourceCount;
55};
56
57typedef struct __DASession __DASession;
58
59static CFStringRef __DASessionCopyDescription( CFTypeRef object );
60static CFStringRef __DASessionCopyFormattingDescription( CFTypeRef object, CFDictionaryRef );
61static void        __DASessionDeallocate( CFTypeRef object );
62static Boolean     __DASessionEqual( CFTypeRef object1, CFTypeRef object2 );
63static CFHashCode  __DASessionHash( CFTypeRef object );
64
65static const CFRuntimeClass __DASessionClass =
66{
67    0,
68    "DASession",
69    NULL,
70    NULL,
71    __DASessionDeallocate,
72    __DASessionEqual,
73    __DASessionHash,
74    __DASessionCopyFormattingDescription,
75    __DASessionCopyDescription
76};
77
78static CFTypeID __kDASessionTypeID = _kCFRuntimeNotATypeID;
79
80static pthread_mutex_t __gDASessionSetAuthorizationLock = PTHREAD_MUTEX_INITIALIZER;
81
82const CFStringRef kDAApprovalRunLoopMode = CFSTR( "kDAApprovalRunLoopMode" );
83
84__private_extern__ void _DADispatchCallback( DASessionRef    session,
85                                             void *          address,
86                                             void *          context,
87                                             _DACallbackKind kind,
88                                             CFTypeRef       argument0,
89                                             CFTypeRef       argument1 );
90
91__private_extern__ void _DAInitialize( void );
92
93static CFStringRef __DASessionCopyDescription( CFTypeRef object )
94{
95    DASessionRef session = ( DASessionRef ) object;
96
97    return CFStringCreateWithFormat( CFGetAllocator( object ),
98                                     NULL,
99                                     CFSTR( "<DASession %p [%p]>{id = %s [%d]:%d}" ),
100                                     object,
101                                     CFGetAllocator( object ),
102                                     session->_name,
103                                     session->_pid,
104                                     session->_server );
105}
106
107static CFStringRef __DASessionCopyFormattingDescription( CFTypeRef object, CFDictionaryRef options )
108{
109    DASessionRef session = ( DASessionRef ) object;
110
111    return CFStringCreateWithFormat( CFGetAllocator( object ),
112                                     NULL,
113                                     CFSTR( "%s [%d]:%d" ),
114                                     session->_name,
115                                     session->_pid,
116                                     session->_server );
117}
118
119static DASessionRef __DASessionCreate( CFAllocatorRef allocator )
120{
121    __DASession * session;
122
123    session = ( void * ) _CFRuntimeCreateInstance( allocator, __kDASessionTypeID, sizeof( __DASession ) - sizeof( CFRuntimeBase ), NULL );
124
125    if ( session )
126    {
127        session->_authorization = NULL;
128        session->_client        = NULL;
129        session->_name          = NULL;
130        session->_pid           = 0;
131        session->_server        = MACH_PORT_NULL;
132        session->_source        = NULL;
133        session->_source2       = NULL;
134        session->_sourceCount   = 0;
135    }
136
137    return session;
138}
139
140static void __DASessionDeallocate( CFTypeRef object )
141{
142    DASessionRef session = ( DASessionRef ) object;
143
144    assert( session->_client  == NULL );
145    assert( session->_source  == NULL );
146    assert( session->_source2 == NULL );
147
148    if ( session->_authorization )  AuthorizationFree( session->_authorization, kAuthorizationFlagDefaults );
149    if ( session->_name          )  free( session->_name );
150    if ( session->_server        )  mach_port_deallocate( mach_task_self( ), session->_server );
151}
152
153static Boolean __DASessionEqual( CFTypeRef object1, CFTypeRef object2 )
154{
155    DASessionRef session1 = ( DASessionRef ) object1;
156    DASessionRef session2 = ( DASessionRef ) object2;
157
158    return ( session1->_server == session2->_server ) ? TRUE : FALSE;
159}
160
161static CFHashCode __DASessionHash( CFTypeRef object )
162{
163    DASessionRef session = ( DASessionRef ) object;
164
165    return ( CFHashCode ) session->_server;
166}
167
168__private_extern__ void _DASessionCallback( CFMachPortRef port, void * message, CFIndex messageSize, void * info )
169{
170    vm_address_t           _queue;
171    mach_msg_type_number_t _queueSize;
172    DASessionRef           session = info;
173    kern_return_t          status;
174
175    status = _DAServerSessionCopyCallbackQueue( session->_server, &_queue, &_queueSize );
176
177    if ( status == KERN_SUCCESS )
178    {
179        CFArrayRef queue;
180
181        queue = _DAUnserializeWithBytes( CFGetAllocator( session ), _queue, _queueSize );
182
183        if ( queue )
184        {
185            CFIndex count;
186            CFIndex index;
187
188            count = CFArrayGetCount( queue );
189
190            for ( index = 0; index < count; index++ )
191            {
192                CFDictionaryRef callback;
193
194                callback = CFArrayGetValueAtIndex( queue, index );
195
196                if ( callback )
197                {
198                    void * address;
199                    void * context;
200
201                    CFTypeRef argument0;
202                    CFTypeRef argument1;
203
204                    address = ( void * ) ( uintptr_t ) ___CFDictionaryGetIntegerValue( callback, _kDACallbackAddressKey );
205                    context = ( void * ) ( uintptr_t ) ___CFDictionaryGetIntegerValue( callback, _kDACallbackContextKey );
206
207                    argument0 = CFDictionaryGetValue( callback, _kDACallbackArgument0Key );
208                    argument1 = CFDictionaryGetValue( callback, _kDACallbackArgument1Key );
209
210                    _DADispatchCallback( session, address, context, ___CFDictionaryGetIntegerValue( callback, _kDACallbackKindKey ), argument0, argument1 );
211                }
212            }
213
214            CFRelease( queue );
215        }
216
217        vm_deallocate( mach_task_self( ), _queue, _queueSize );
218    }
219}
220
221__private_extern__ AuthorizationRef _DASessionGetAuthorization( DASessionRef session )
222{
223    AuthorizationRef authorization;
224
225    pthread_mutex_lock( &__gDASessionSetAuthorizationLock );
226
227    authorization = session->_authorization;
228
229    if ( authorization == NULL )
230    {
231        kern_return_t status;
232
233        /*
234         * Create the session's authorization reference.
235         */
236
237        status = AuthorizationCreate( NULL, NULL, kAuthorizationFlagDefaults, &authorization );
238
239        if ( status == errAuthorizationSuccess )
240        {
241            AuthorizationExternalForm _authorization;
242
243            /*
244             * Create the session's authorization reference representation.
245             */
246
247            status = AuthorizationMakeExternalForm( authorization, &_authorization );
248
249            if ( status == errAuthorizationSuccess )
250            {
251                _DAServerSessionSetAuthorization( session->_server, _authorization );
252
253                session->_authorization = authorization;
254            }
255            else
256            {
257                AuthorizationFree( authorization, kAuthorizationFlagDefaults );
258
259                authorization = NULL;
260            }
261        }
262    }
263
264    pthread_mutex_unlock( &__gDASessionSetAuthorizationLock );
265
266    return authorization;
267}
268
269#ifndef __LP64__
270
271__private_extern__ mach_port_t _DASessionGetClientPort( DASessionRef session )
272{
273    mach_port_t client;
274
275    client = MACH_PORT_NULL;
276
277    if ( session->_client )
278    {
279        client = CFMachPortGetPort( session->_client );
280    }
281
282    return client;
283}
284
285#endif /* !__LP64__ */
286
287__private_extern__ mach_port_t _DASessionGetID( DASessionRef session )
288{
289    return session->_server;
290}
291
292__private_extern__ void _DASessionInitialize( void )
293{
294    __kDASessionTypeID = _CFRuntimeRegisterClass( &__DASessionClass );
295}
296
297__private_extern__ void _DASessionScheduleWithRunLoop( DASessionRef session )
298{
299    session->_sourceCount++;
300
301    if ( session->_sourceCount == 1 )
302    {
303        mach_port_t   clientPort;
304        kern_return_t status;
305
306        /*
307         * Create the session's client port.
308         */
309
310        status = mach_port_allocate( mach_task_self( ), MACH_PORT_RIGHT_RECEIVE, &clientPort );
311
312        if ( status == KERN_SUCCESS )
313        {
314            mach_port_limits_t limits = { 0 };
315
316            limits.mpl_qlimit = 1;
317
318            /*
319             * Set up the session's client port.  It requires no more than one queue element.
320             */
321
322            status = mach_port_set_attributes( mach_task_self( ),
323                                               clientPort,
324                                               MACH_PORT_LIMITS_INFO,
325                                               ( mach_port_info_t ) &limits,
326                                               MACH_PORT_LIMITS_INFO_COUNT );
327
328            if ( status == KERN_SUCCESS )
329            {
330                CFMachPortRef     client;
331                CFMachPortContext clientContext;
332
333                clientContext.version         = 0;
334                clientContext.info            = session;
335                clientContext.retain          = CFRetain;
336                clientContext.release         = CFRelease;
337                clientContext.copyDescription = NULL;
338
339                /*
340                 * Create the session's client port run loop source.
341                 */
342
343                client = CFMachPortCreateWithPort( CFGetAllocator( session ), clientPort, _DASessionCallback, &clientContext, NULL );
344
345                if ( client )
346                {
347                    CFRunLoopSourceRef source;
348
349                    source = CFMachPortCreateRunLoopSource( CFGetAllocator( session ), client, 0 );
350
351                    if ( source )
352                    {
353                        session->_client = client;
354                        session->_source = source;
355
356                        _DAServerSessionSetClientPort( session->_server, CFMachPortGetPort( client ) );
357
358                        return;
359                    }
360
361                    CFMachPortInvalidate( client );
362
363                    CFRelease( client );
364                }
365            }
366
367            mach_port_mod_refs( mach_task_self( ), clientPort, MACH_PORT_RIGHT_RECEIVE, -1 );
368        }
369    }
370}
371
372__private_extern__ void _DASessionUnscheduleFromRunLoop( DASessionRef session )
373{
374    if ( session->_sourceCount == 1 )
375    {
376        if ( session->_source )
377        {
378            CFRunLoopSourceInvalidate( session->_source );
379
380            CFRelease( session->_source );
381
382            session->_source = NULL;
383        }
384
385        if ( session->_client )
386        {
387            mach_port_t clientPort;
388
389            clientPort = CFMachPortGetPort( session->_client );
390
391            CFMachPortInvalidate( session->_client );
392
393            CFRelease( session->_client );
394
395            mach_port_mod_refs( mach_task_self( ), clientPort, MACH_PORT_RIGHT_RECEIVE, -1 );
396
397            session->_client = NULL;
398        }
399    }
400
401    if ( session->_sourceCount )
402    {
403        session->_sourceCount--;
404    }
405}
406
407DAApprovalSessionRef DAApprovalSessionCreate( CFAllocatorRef allocator )
408{
409    return DASessionCreate( allocator );
410}
411
412CFTypeID DAApprovalSessionGetTypeID( void )
413{
414    return DASessionGetTypeID( );
415}
416
417void DAApprovalSessionScheduleWithRunLoop( DAApprovalSessionRef session, CFRunLoopRef runLoop, CFStringRef runLoopMode )
418{
419    DASessionScheduleWithRunLoop( session, runLoop, kDAApprovalRunLoopMode );
420
421    DASessionScheduleWithRunLoop( session, runLoop, runLoopMode );
422}
423
424void DAApprovalSessionUnscheduleFromRunLoop( DAApprovalSessionRef session, CFRunLoopRef runLoop, CFStringRef runLoopMode )
425{
426    DASessionUnscheduleFromRunLoop( session, runLoop, kDAApprovalRunLoopMode );
427
428    DASessionUnscheduleFromRunLoop( session, runLoop, runLoopMode );
429}
430
431DASessionRef DASessionCreate( CFAllocatorRef allocator )
432{
433    DASessionRef session;
434
435    /*
436     * Initialize the Disk Arbitration framework.
437     */
438
439    _DAInitialize( );
440
441    /*
442     * Create the session.
443     */
444
445    session = __DASessionCreate( allocator );
446
447    if ( session )
448    {
449        mach_port_t   bootstrapPort;
450        kern_return_t status;
451
452        /*
453         * Obtain the bootstrap port.
454         */
455
456        status = task_get_bootstrap_port( mach_task_self( ), &bootstrapPort );
457
458        if ( status == KERN_SUCCESS )
459        {
460            mach_port_t masterPort;
461
462            /*
463             * Obtain the Disk Arbitration master port.
464             */
465
466            status = bootstrap_look_up2( bootstrapPort, _kDADaemonName, &masterPort, 0, BOOTSTRAP_PRIVILEGED_SERVER );
467
468            mach_port_deallocate( mach_task_self( ), bootstrapPort );
469
470            if ( status == KERN_SUCCESS )
471            {
472                mach_port_t server;
473
474                /*
475                 * Create the session at the server.
476                 */
477
478                status = _DAServerSessionCreate( masterPort,
479                                                 basename( ( char * ) _dyld_get_image_name( 0 ) ),
480                                                 getpid( ),
481                                                 &server );
482
483                mach_port_deallocate( mach_task_self( ), masterPort );
484
485                if ( status == KERN_SUCCESS )
486                {
487                    session->_name   = strdup( basename( ( char * ) _dyld_get_image_name( 0 ) ) );
488                    session->_pid    = getpid( );
489                    session->_server = server;
490
491///w:start
492if ( strcmp( session->_name, "SystemUIServer" ) == 0 )
493{
494    _DASessionGetAuthorization( session );
495}
496///w:stop
497                    return session;
498                }
499            }
500        }
501
502        CFRelease( session );
503    }
504
505    return NULL;
506}
507
508CFTypeID DASessionGetTypeID( void )
509{
510    return __kDASessionTypeID;
511}
512
513void DASessionScheduleWithRunLoop( DASessionRef session, CFRunLoopRef runLoop, CFStringRef runLoopMode )
514{
515    if ( session )
516    {
517        _DASessionScheduleWithRunLoop( session );
518
519        if ( session->_source )
520        {
521            CFRunLoopAddSource( runLoop, session->_source, runLoopMode );
522        }
523    }
524}
525
526void DASessionSetDispatchQueue( DASessionRef session, dispatch_queue_t queue )
527{
528    if ( session )
529    {
530        if ( session->_source2 )
531        {
532            dispatch_source_cancel( session->_source2 );
533
534            dispatch_release( session->_source2 );
535
536            session->_source2 = NULL;
537        }
538
539        if ( queue )
540        {
541            mach_port_t   client;
542            kern_return_t status;
543
544            /*
545             * Create the session's client port.
546             */
547
548            status = mach_port_allocate( mach_task_self( ), MACH_PORT_RIGHT_RECEIVE, &client );
549
550            if ( status == KERN_SUCCESS )
551            {
552                mach_port_limits_t limits = { 0 };
553
554                limits.mpl_qlimit = 1;
555
556                /*
557                 * Set up the session's client port.  It requires no more than one queue element.
558                 */
559
560                status = mach_port_set_attributes( mach_task_self( ),
561                                                   client,
562                                                   MACH_PORT_LIMITS_INFO,
563                                                   ( mach_port_info_t ) &limits,
564                                                   MACH_PORT_LIMITS_INFO_COUNT );
565
566                if ( status == KERN_SUCCESS )
567                {
568                    dispatch_source_t source;
569
570                    /*
571                     * Create the session's client port dispatch source.
572                     */
573
574                    source = dispatch_source_create( DISPATCH_SOURCE_TYPE_MACH_RECV, client, 0, queue );
575
576                    if ( source )
577                    {
578                        session->_source2 = source;
579
580                        CFRetain( session );
581
582                        dispatch_source_set_cancel_handler( session->_source2, ^
583                        {
584                            mach_port_mod_refs( mach_task_self( ), client, MACH_PORT_RIGHT_RECEIVE, -1 );
585
586                            CFRelease( session );
587                        } );
588
589                        dispatch_source_set_event_handler( session->_source2, ^
590                        {
591                            mach_msg_empty_rcv_t message;
592
593                            mach_msg( ( void * ) &message, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof( message ), client, 0, MACH_PORT_NULL );
594
595                            _DASessionCallback( NULL, NULL, 0, session );
596                        } );
597
598                        dispatch_resume( session->_source2 );
599
600                        _DAServerSessionSetClientPort( session->_server, client );
601
602                        return;
603                    }
604                }
605
606                mach_port_mod_refs( mach_task_self( ), client, MACH_PORT_RIGHT_RECEIVE, -1 );
607            }
608        }
609    }
610}
611
612void DASessionUnscheduleFromRunLoop( DASessionRef session, CFRunLoopRef runLoop, CFStringRef runLoopMode )
613{
614    if ( session )
615    {
616        if ( session->_source )
617        {
618            CFRunLoopRemoveSource( runLoop, session->_source, runLoopMode );
619        }
620
621        _DASessionUnscheduleFromRunLoop( session );
622    }
623}
624