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