DldIOShadowFile* DldIOShadowFile::withFileName( __in char* name, __in size_t nameLength ) { assert( preemption_enabled() ); // // check for the correct file format, the name must be a full path // if( 0x0 == nameLength || L'\0' != name[ nameLength - 0x1 ] || L'/' != name[0] ){ DBG_PRINT_ERROR(("an invalid file name format\n")); return NULL; } DldIOShadowFile* newFile = new DldIOShadowFile(); assert( newFile ); if( !newFile ) return NULL; if( !newFile->initWithFileName( name, nameLength ) ){ newFile->release(); return NULL; } return newFile; }
IOReturn DldIPCUserClient::ProcessResponse( __in DldDriverEventData* response ) { UInt32 waitBlockIndex = response->Header.waitBlockIndex; if( DLD_EVENT_RESPONSE != response->Header.type ){ DBG_PRINT_ERROR(("DLD_EVENT_RESPONSE != response->Header.type\n")); return kIOReturnBadArgument; } if( waitBlockIndex >= DLD_STATIC_ARRAY_SIZE(DldIPCUserClient::WaitBlocks) ){ DBG_PRINT_ERROR(("response->Head.waitBlockIndex is out of range\n")); return kIOReturnBadArgument; } if( DldIPCUserClient::WaitBlocks[waitBlockIndex].eventID != response->Header.id ){ DBG_PRINT_ERROR(("DldIPCUserClient::WaitBlocks[ %d ].eventID != response->Header.id\n", waitBlockIndex)); return kIOReturnBadArgument; } bool wakeUp = false; IOSimpleLockLock( DldIPCUserClient::WaitLock ); { // start of the locked region if( DldIPCUserClient::WaitBlocks[waitBlockIndex].eventID == response->Header.id ){ assert( 0x0 != DldIPCUserClient::WaitBlocks[waitBlockIndex].inUse ); DldIPCUserClient::WaitBlocks[waitBlockIndex].completed = true; DldIPCUserClient::WaitBlocks[waitBlockIndex].operationCompletionStatus = response->Tail.Response.operationCompletionStatus; wakeUp = true; } } // end of the locked region IOSimpleLockUnlock( DldIPCUserClient::WaitLock ); if( ! wakeUp ) return kIOReturnBadArgument; // // there is a slim chance to wake up a just acquired block that was released by timeout, we neglect this case // wakeup( (event_t)&DldIPCUserClient::WaitBlocks[waitBlockIndex] ); return kIOReturnSuccess; }
DldMacPolicy* DldMacPolicy::createPolicyObject( __in struct mac_policy_conf *mpc, __in void* xd ) { assert( preemption_enabled() ); DldMacPolicy* newObject = new DldMacPolicy(); assert( newObject ); if( !newObject ){ DBG_PRINT_ERROR(("new DldMacPolicy() failed\n")); return NULL; } if( !newObject->initWithPolicyConf( mpc, xd ) ){ DBG_PRINT_ERROR(("newObject->initWithPolicyConf( mpc, xd ) failed\n")); newObject->release(); return NULL; } return newObject; }
IOReturn com_VFSFilter0::unregisterUserClient( __in VFSFilter0UserClient* client ) { bool unregistered; VFSFilter0UserClient* currentClient; currentClient = (VFSFilter0UserClient*)this->userClient; assert( currentClient == client ); if( currentClient != client ){ DBG_PRINT_ERROR(("currentClient != client\n")); return kIOReturnError; } this->pendingUnregistration = true; unregistered = OSCompareAndSwapPtr( (void*)currentClient, NULL, &this->userClient ); assert( unregistered && NULL == this->userClient ); if( !unregistered ){ DBG_PRINT_ERROR(("!unregistered\n")); this->pendingUnregistration = false; return kIOReturnError; } do { // wait for any existing client invocations to return struct timespec ts = { 1, 0 }; // one second (void)msleep( &this->clientInvocations, // wait channel NULL, // mutex PUSER, // priority "com_VFSFilter0::unregisterUserClient()", // wait message &ts ); // sleep interval } while( this->clientInvocations != 0 ); currentClient->release(); this->pendingUnregistration = false; return unregistered? kIOReturnSuccess: kIOReturnError; }
bool DldMacPolicy::initWithPolicyConf( __in struct mac_policy_conf *mpc, __in void* xd ) { if( !this->init() ){ DBG_PRINT_ERROR(("this->init() failed\n")); return false; } this->mpc = mpc; this->xd = xd; return true; }
IOReturn com_VFSFilter0::registerUserClient( __in VFSFilter0UserClient* client ) { bool registered; if( this->pendingUnregistration ){ DBG_PRINT_ERROR(("com_VFSFilter0 : pendingUnregistration\n")); return kIOReturnError; } registered = OSCompareAndSwapPtr( NULL, (void*)client, &this->userClient ); assert( registered ); if( !registered ){ DBG_PRINT_ERROR(("com_VFSFilter0 : a client was not registered\n")); return kIOReturnError; } client->retain(); return registered? kIOReturnSuccess: kIOReturnError; }
DldAclWithProcsObject* DldAclWithProcsObject::withAcl( __in_opt DldAclWithProcs* procsAcl ) { DldAclWithProcsObject* fileProcsAclObj = new DldAclWithProcsObject(); assert( fileProcsAclObj ); if( !fileProcsAclObj ){ DBG_PRINT_ERROR(("new DldAclWithProcsObject() failed\n")); return NULL; } if( !fileProcsAclObj->init() ){ DBG_PRINT_ERROR(("fileProcsAclObj->init() failed\n")); fileProcsAclObj->release(); return NULL; } if( procsAcl ){ // // allocate a memory and copy ACL, a caller must free an ACL provided as a parameter // fileProcsAclObj->acl = (DldAclWithProcs*)IOMalloc( DldAclWithProcsSize( procsAcl ) ); assert( fileProcsAclObj->acl ); if( !fileProcsAclObj->acl ){ DBG_PRINT_ERROR(("IOMalloc( DldAclWithProcsSize( procsAcl ) ) failed\n")); fileProcsAclObj->release(); return NULL; } bcopy( procsAcl, fileProcsAclObj->acl, DldAclWithProcsSize( procsAcl ) ); } // end if( procsAcl ) return fileProcsAclObj; }
bool DldIOKitHookDictionaryEntry::init() { if( !super::init() ) return false; this->NotifiersDictionary = OSDictionary::withCapacity(2); assert( this->NotifiersDictionary ); if( !this->NotifiersDictionary ) { DBG_PRINT_ERROR( ( "DldIOKitHookDictionaryEntry(%s)::init() OSDictionary::withCapacity(2) failed\n", this->IOKitClassName->getCStringNoCopy() ) ); return false; } this->NotifiersDictionaryIterator = OSCollectionIterator::withCollection( this->NotifiersDictionary ); assert( this->NotifiersDictionaryIterator ); if( !this->NotifiersDictionaryIterator ){ DBG_PRINT_ERROR( ( "DldIOKitHookDictionaryEntry(%s)::init() OSDictionary::withCapacity(2) failed\n", this->IOKitClassName->getCStringNoCopy() ) ); return false; } return true; }
bool DldIOKitHookDictionaryEntry::setNotifier( __in const OSSymbol * type, __in IONotifier* ObjectNotifier ) { assert( ObjectNotifier ); assert( !this->NotifiersDictionary->getObject( type ) ); if( !this->NotifiersDictionary->setObject( type, (OSMetaClassBase*)ObjectNotifier ) ){ assert( !"DldIOKitHookDictionaryEntry::setNotifier failed" ); DBG_PRINT_ERROR( ( "DldIOKitHookDictionaryEntry(%s)::setPublisNotifier( %s ) failed\n", this->IOKitClassName->getCStringNoCopy(), type->getCStringNoCopy() ) ); return false; } return true; }
kern_return_t DldMacPolicy::unRegisterMacPolicy() { kern_return_t err = ENOENT; assert( preemption_enabled() ); if( this->registered ){ err = mac_policy_unregister( this->handlep ); this->registered = !( KERN_SUCCESS == err ); assert( KERN_SUCCESS == err ); if( KERN_SUCCESS != err ){ DBG_PRINT_ERROR(("unRegisterMacPolicy failed with err = %d\n", err)); } } return err; }
kern_return_t DldMacPolicy::registerMacPolicy() { kern_return_t err; assert( preemption_enabled() ); assert( !this->registered ); if( this->registered ) return KERN_SUCCESS; err = mac_policy_register( this->mpc, &this->handlep, this->xd ); this->registered = ( KERN_SUCCESS == err ); assert( KERN_SUCCESS == err ); if( KERN_SUCCESS != err ){ DBG_PRINT_ERROR(("registerMacPolicy failed with err = %d\n", err)); } return err; }
DldIOKitHookDictionaryEntry* DldIOKitHookDictionaryEntry::withIOKitClassName( __in OSString* IOKitClassName, __in DldHookDictionaryEntryData* HookData ) { assert( NULL != IOKitClassName ); assert( NULL != HookData ); assert( NULL != HookData->HookFunction ); assert( NULL != HookData->UnHookFunction ); DldIOKitHookDictionaryEntry* pEntry = new DldIOKitHookDictionaryEntry(); assert( NULL != pEntry ); if( NULL == pEntry ) return NULL; // // init the IOKit base classes // if( !pEntry->init() ){ assert( !"DldIOKitHookDictionaryEntry: class init failed" ); DBG_PRINT_ERROR( ( "DldIOKitHookDictionaryEntry::withIOKitClassName( %s ) pEntry->init() failed\n", IOKitClassName->getCStringNoCopy() ) ); pEntry->release(); return NULL; } IOKitClassName->retain(); pEntry->IOKitClassName = IOKitClassName; pEntry->HookData = *HookData; return pEntry; }
IOReturn DldIOShadowFile::write( __in void* data, __in off_t length, __in_opt off_t offsetForShadowFile ) { /*! @function vn_rdwr @abstract Read from or write to a file. @discussion vn_rdwr() abstracts the details of constructing a uio and picking a vnode operation to allow simple in-kernel file I/O. @param rw UIO_READ for a read, UIO_WRITE for a write. @param vp The vnode on which to perform I/O. @param base Start of buffer into which to read or from which to write data. @param len Length of buffer. @param offset Offset within the file at which to start I/O. @param segflg What kind of address "base" is. See uio_seg definition in sys/uio.h. UIO_SYSSPACE for kernelspace, UIO_USERSPACE for userspace. UIO_USERSPACE32 and UIO_USERSPACE64 are in general preferred, but vn_rdwr will make sure that has the correct address sizes. @param ioflg Defined in vnode.h, e.g. IO_NOAUTH, IO_NOCACHE. @param cred Credential to pass down to filesystem for authentication. @param aresid Destination for amount of requested I/O which was not completed, as with uio_resid(). @param p Process requesting I/O. @return 0 for success; errors from filesystem, and EIO if did not perform all requested I/O and the "aresid" parameter is NULL. */ assert( preemption_enabled() ); assert( length < 0x0FFFFFFF00000000ll ); off_t offset = offsetForShadowFile; if( DLD_IGNR_FSIZE == offsetForShadowFile && !this->reserveOffset( length, &offset ) ){ DBG_PRINT_ERROR(("A quota has been exceeded for the %s file, the quota is %llu \n", this->path, this->maxSize )); return kIOReturnNoResources; } #if defined( DBG ) static UInt32 gWriteCount = 0x0; UInt32 writeCount = OSIncrementAtomic( &gWriteCount ) + 0x1; #endif//DBG int error = 0x0; off_t bytesWritten = 0x0; while( !error && length != bytesWritten ){ unsigned int bytesToWrite; #if defined( DBG ) // // write by 32МВ chunks for each 2nd function invocation, and 1GB for others // if( (length - bytesWritten) > ((writeCount%2)? 0x40000000ll : 0x2000000ll) ) bytesToWrite = (writeCount%2)? 0x40000000ll : 0x2000000ll; else bytesToWrite = (int)(length - bytesWritten); #else // // write by 1GB chunks // if( (length - bytesWritten) > 0x40000000ll ) bytesToWrite = 0x40000000; else bytesToWrite = (int)(length - bytesWritten); #endif//!DBG error = vn_rdwr( UIO_WRITE, this->vnode, (char*)data, bytesToWrite, offset + bytesWritten, UIO_SYSSPACE, IO_NOAUTH | IO_SYNC,//IO_UNIT, kauth_cred_get(), NULL, current_proc() ); if( !error ) bytesWritten = bytesWritten + bytesToWrite; else { DBG_PRINT_ERROR(("vn_rdwr( %s, %u ) failed with the 0x%X error\n", this->path, bytesToWrite, error)); } } return error; }
bool DldIOShadowFile::initWithFileName( __in char* name, __in size_t nameLength ) { assert( preemption_enabled() ); if( nameLength > (MAXPATHLEN + sizeof( L'\0' )) ){ DBG_PRINT_ERROR(("length > (MAXPATHLEN + sizeof( L'\\0' ))\n")); return false; } if( !super::init() ){ DBG_PRINT_ERROR(("super::init() failed\n")); return false; } this->rwLock = IORWLockAlloc(); assert( this->rwLock ); if( !this->rwLock ){ DBG_PRINT_ERROR(("this->rwLock = IORWLockAlloc() failed\n" )); return false; } // // set a maximum file size to infinity // this->maxSize = DLD_IGNR_FSIZE; // // set switch size to 512 MB // this->switchSize = 0x20000000ll; this->path = (char*)IOMalloc( nameLength ); assert( this->path ); if( !this->path ){ DBG_PRINT_ERROR(("this->path = IOMalloc( %u ) failed\n", (int)nameLength )); return false; } this->pathLength = nameLength; memcpy( this->path, name, nameLength ); assert( L'\0' == this->path[ this->pathLength - 0x1 ] ); // // open or create the file // errno_t error; vfs_context_t vfs_context; vfs_context = vfs_context_create( NULL ); assert( vfs_context ); if( !vfs_context ) return false; // // open or create a file, truncate if the file exists // error = vnode_open( this->path, O_EXLOCK | O_RDWR | O_CREAT | O_TRUNC | O_SYNC, // fmode 0644,// cmode 0x0,// flags &this->vnode, vfs_context ); vfs_context_rele( vfs_context ); if( error ){ DBG_PRINT_ERROR(("vnode_open() failed with the %u error\n", error)); return false; } // // vn_open returns with both a use_count // and an io_count on the found vnode // // // mark as an internal non shadowed vnode, use the CreateAndAddIOVnodByBSDVnode // as the kauth callback might have not been registered so the vnode open // was not seen by the DL driver // DldIOVnode* dldVnode; dldVnode = DldVnodeHashTable::sVnodesHashTable->CreateAndAddIOVnodByBSDVnode( this->vnode ); assert( dldVnode ); if( !dldVnode ){ // // we can't use this vnode as will be unable to distinguish // the DL internal writes from the user writes this will result // in an endless loop // DBG_PRINT_ERROR(("RetrieveReferencedIOVnodByBSDVnode() failed\n")); this->release(); return NULL; } // // mark as skipped for log, shadowing and any other operations // dldVnode->flags.internal = 0x1; dldVnode->release(); DLD_DBG_MAKE_POINTER_INVALID( dldVnode ); this->expandFile(); // // mark as noncached vnode, writes must be sector aligned! // //vnode_setnocache( this->vnode ); return true; }
// // if fase is returned the request has been completed as the access is denied // bool DldIOSCSIProtocolInterface::ExecuteCommand( __in IOSCSIProtocolInterface* service, __in SCSITaskIdentifier request ) { //SCSICommandDescriptorBlock cdb; //assert( sizeof( cdb ) == kSCSICDBSize_Maximum ); //DldSCSITaskGetCommandDescriptorBlock( request, &cdb ); /////// /// start of the test /* DldSCSITaskCompleteAccessDenied( request ); return false; */ /// end of the test //////// /* dld_classic_rights_t access; access = DldSCSITaskGetCdbRequestedAccess( request ); if( 0x0 != ( DEVICE_WRITE & access ) ){ DldSCSITaskCompleteAccessDenied( request ); return false; }*/ assert( preemption_enabled() ); DldIOService* dldSCSIDev; DldObjectPropertyEntry* property; dld_classic_rights_t requestedAccess; DldAccessCheckParam param; bool disable = false; bool isShadowedUser = false; requestedAccess = DldSCSITaskGetCdbRequestedAccess( request ); if( 0x0 == requestedAccess ) return true; dldSCSIDev = DldIOService::RetrieveDldIOServiceForIOService( service ); assert( dldSCSIDev ); if( !dldSCSIDev ){ DBG_PRINT_ERROR(("DldIOService::RetrieveDldIOServiceForIOService( 0x%p ) returned NULL\n", service )); return true; } // // do not check read operations if there is a CD or DVD object in the stack, // as read requests are generated by every client including a mounted FSD( i.e. BSD susbsystem ), // but if there is no CD or DVD object then a user mode client dismantled them and // attached directly to the SCSI object, in that case the read requests must be // checked and audited // if( DEVICE_READ == requestedAccess ){ DldObjectPropertyEntry* media; // // look for actual CD/DVD properties // media = dldSCSIDev->retrievePropertyByTypeRef( DldObjectPopertyType_IODVDMedia ); if( NULL == media ) media = dldSCSIDev->retrievePropertyByTypeRef( DldObjectPopertyType_IOCDMedia ); if( media ){ // // a request from the BSD subssytem, the request has been checked by the BSD KAUTH callbacks // media->release(); goto __exit; } } // end if( DEVICE_READ == requestedAccess ) property = dldSCSIDev->getObjectProperty(); if( !property ){ // // we are not interested in this device, this means that // there is another IOSCSIProtocolInterface device attached // to this one and this last is visible for user apps // goto __exit; } if( DldObjectPopertyType_SCSIPeripheralDeviceType05 != property->dataU.property->typeDsc.type ){ DBG_PRINT_ERROR(("It seems you added a new SCSI type %i but forgot to add its processing\n", property->dataU.property->typeDsc.type )); assert( !"It seems you added a new SCSI type but forgot to add its processing" ); goto __exit; } bzero( ¶m, sizeof( param ) ); param.userSelectionFlavor = kDefaultUserSelectionFlavor; param.aclType = kDldAclTypeSecurity; param.checkParentType = true; param.dldRequestedAccess.winRequestedAccess = requestedAccess; //param.dldRequestedAccess.kauthRequestedAccess = requestedAccess; // TO DO remove when the daemon will be able to proide ACL param.credential = NULL;// current thread's user param.dldIOService = dldSCSIDev; #if defined(LOG_ACCESS) param.sourceFunction = __PRETTY_FUNCTION__; param.sourceFile = __FILE__; param.sourceLine = __LINE__; #endif//#if defined(LOG_ACCESS) /*if( dldIOService->getObjectProperty() && DLD_DEVICE_TYPE_REMOVABLE == dldIOService->getObjectProperty()->dataU.property->deviceType.type.major ){}*/ ::DldAcquireResources( ¶m ); { ::isAccessAllowed( ¶m ); disable = ( param.output.access.result[ DldFullTypeFlavor ].disable || param.output.access.result[ DldMajorTypeFlavor ].disable || param.output.access.result[ DldParentTypeFlavor ].disable ); #if defined( DBG ) /*if( disable ){ __asm__ volatile( "int $0x3" ); }*/ #endif // // should we log? // if( param.output.access.result[ DldFullTypeFlavor ].log || param.output.access.result[ DldMajorTypeFlavor ].log ){ DldDriverDataLogInt intData; DldDriverDataLog data; bool logDataValid; kauth_cred_t credentials; intData.logDataSize = sizeof( data ); intData.logData = &data; credentials = kauth_cred_proc_ref( current_proc() ); // just like in isAccessAllowed() assert( credentials ); if( credentials ){ logDataValid = dldSCSIDev->initDeviceLogData( ¶m.dldRequestedAccess, (ALL_WRITE & requestedAccess) ? DldFileOperationWrite : DldFileOperationRead, proc_pid(current_proc()), credentials, ( param.output.access.result[ DldFullTypeFlavor ].disable || param.output.access.result[ DldMajorTypeFlavor ].disable ), &intData ); assert( logDataValid ); if( logDataValid ){ gLog->logData( &intData ); } else { DBG_PRINT_ERROR(("device log data is invalid\n")); } kauth_cred_unref( &credentials ); } else { DBG_PRINT_ERROR(("kauth_cred_proc_ref failed\n")); } }// end log } ::DldReleaseResources( ¶m ); if( !disable ){ // // check for ejection // if( DldSCSITaskIsMediaEjectionRequest( request ) ){ /* a media is ejected by calling IOSCSIMultimediaCommandsDevice::EjectTheMedia which in turn calls IOSCSIProtocolInterface::ExecuteCommand with two commands - the first unlocks the tray and the second is kSCSICmd_START_STOP_UNIT, the stack is as follows #0 0x015eae43 in IOSCSIMultimediaCommandsDevice::EjectTheMedia (this=0x57c2d00) at /SourceCache/IOSCSIArchitectureModelFamily/IOSCSIArchitectureModelFamily-265.0.3/IOSCSIMultimediaCommands/IOSCSIMultimediaCommandsDevice.cpp:722 #1 0x015f0cb3 in IODVDServices::doEjectMedia (this=0x57c6380) at /SourceCache/IOSCSIArchitectureModelFamily/IOSCSIArchitectureModelFamily-265.0.3/IOSCSIMultimediaCommands/IODVDServices.cpp:594 #2 0x011edfd8 in IOBlockStorageDriver::ejectMedia (this=0x57c2200) at /SourceCache/IOStorageFamily/IOStorageFamily-116.1/IOBlockStorageDriver.cpp:1191 #3 0x011f70ef in dkioctl (dev=234881028, cmd=536896533, data=0x31d93e94 "", flags=1, proc=0x5bf4d20) at /SourceCache/IOStorageFamily/IOStorageFamily-116.1/IOMediaBSDClient.cpp:1382 #4 0x003662a1 in spec_ioctl (ap=0x31d93d9c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/miscfs/specfs/spec_vnops.c:529 #5 0x00357488 in VNOP_IOCTL (vp=0x8f612e4, command=536896533, data=0x31d93e94 "", fflag=1, ctx=0x31d93e8c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/vfs/kpi_vfs.c:3606 #6 0x0034ca2f in vn_ioctl (fp=0x63504a0, com=536896533, data=0x31d93e94 "", ctx=0x31d93e8c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/vfs/vfs_vnops.c:1147 #7 0x00515c35 in fo_ioctl (fp=0x63504a0, com=536896533, data=0x31d93e94 "", ctx=0x31d93e8c) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/kern/kern_descrip.c:4826 #8 0x005458ff in ioctl (p=0x5bf4d20, uap=0x54bea28, retval=0x5680324) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/kern/sys_generic.c:914 #9 0x005b19b5 in unix_syscall64 (state=0x54bea24) at /work/Mac_OS_X_kernel/10_6_4/xnu-1504.7.4/bsd/dev/i386/systemcalls.c:365 a description for the START_STOP_UNIT command can be found here http://en.wikipedia.org/wiki/SCSI_Start_Stop_Unit_Command we are interested in the following field LoEj (load/eject) and Start - these two bits are used together: 00 - Stop motor 01 - Start motor 10 - Eject media 11 - Load media */ // // the media is being removed, invalidate the CD/DVD white list state, // the FSD has been already unmounted as the tray is locked when FSD // is mounted, if the white list results will be removed prematurely // when they will be restored on the next request // DldAclObject* wlACLToRemove; property->dataU.property->LockExclusive(); { // start of the lock wlACLToRemove = property->dataU.property->whiteListState.acl; property->dataU.property->whiteListState.acl = NULL; property->dataU.property->whiteListState.inWhiteList = false; property->dataU.property->whiteListState.currentWLApplied = false;// this value is a main driver to start reinitialization // // meaningless for DVD/CD // assert( false == property->dataU.property->whiteListState.propagateUp ); if( DldObjectPopertyType_SCSIPeripheralDeviceType05 == property->dataU.property->typeDsc.type ){ // // the media is being removed // property->dataU.ioSCSIPeripheralType05Property->uidValid = false; bzero( &property->dataU.ioSCSIPeripheralType05Property->mediaUID, sizeof( property->dataU.ioSCSIPeripheralType05Property->mediaUID ) ); } }// end of the lock property->dataU.property->UnLockExclusive(); if( wlACLToRemove ) wlACLToRemove->release(); property->setUIDProperty(); }// end if( DldSCSITaskIsMediaEjectionRequest( request ) // // check for shadowing // DldAccessCheckParam param; bzero( ¶m, sizeof( param ) ); param.userSelectionFlavor = kDefaultUserSelectionFlavor; param.aclType = kDldAclTypeShadow; param.checkParentType = true; param.dldRequestedAccess.kauthRequestedAccess = requestedAccess;// actually the value is ignored param.credential = NULL;// current thread's user param.service = NULL; param.dldIOService = dldSCSIDev; ::DldAcquireResources( ¶m ); ::DldIsShadowedUser( ¶m ); ::DldReleaseResources( ¶m ); isShadowedUser = ( param.output.shadow.result[ DldFullTypeFlavor ].shadow || param.output.shadow.result[ DldMajorTypeFlavor ].shadow || param.output.shadow.result[ DldParentTypeFlavor ].shadow ); }// end if( !disable ) // // shadow the write request // if( isShadowedUser && 0x0 != ( DEVICE_WRITE & requestedAccess ) ){ assert( !disable ); DldCommonShadowParams commonParams; UInt32 completionEvent = DLD_INVALID_EVENT_VALUE; IOReturn shadowCompletionRC = kIOReturnInvalid; bool synchronousShadow = false; bool shadowed = false; if( gSecuritySettings.securitySettings.disableOnShadowErrors ){ // // the shadow operation must be synchronous // synchronousShadow = true; } bzero( &commonParams, sizeof( commonParams ) ); commonParams.operationID = gShadow->generateUniqueID(); if( synchronousShadow ){ // // for synchronous processing we need an event to wait for shadow completion // DldInitNotificationEvent( &completionEvent ); commonParams.completionEvent = &completionEvent; commonParams.shadowCompletionRC = &shadowCompletionRC; } // // shadow the request // shadowed = gShadow->shadowSCSIOperation( dldSCSIDev, request, &commonParams ); if( shadowed && synchronousShadow ){ // // wait for shadow completion // DldWaitForNotificationEvent( &completionEvent ); // // the status code must be valid // assert( kIOReturnInvalid != completionEvent ); } else if( shadowed ){ // // an asynchronous shadowing, the first phase was successful // assert( !synchronousShadow ); assert( DLD_INVALID_EVENT_VALUE == completionEvent ); shadowCompletionRC = KERN_SUCCESS; } else { // // failed on the first phase // DBG_PRINT_ERROR(("the first phase of the shadowing failed\n")); shadowCompletionRC = KERN_FAILURE; } if( KERN_SUCCESS != shadowCompletionRC && gSecuritySettings.securitySettings.disableOnShadowErrors ){ // // the shadowing failed so must the request // DBG_PRINT_ERROR(("shadowing failed\n")); disable = true; } // // the SCSI operation completion status is reported by a completion callback hook, see shadowSCSIOperation() // }// end if( isShadowedUser ) __exit: assert( dldSCSIDev ); dldSCSIDev->release(); if( disable ) DldSCSITaskCompleteAccessDenied( request ); return (!disable); }
bool com_VFSFilter0::start( __in IOService *provider ) { //__asm__ volatile( "int $0x3" ); VNodeMap::Init(); if( kIOReturnSuccess != VFSHookInit() ){ DBG_PRINT_ERROR( ( "VFSHookInit() failed\n" ) ); goto __exit_on_error; } if( ! QvrVnodeHooksHashTable::CreateStaticTableWithSize( 8, true ) ){ DBG_PRINT_ERROR( ( "QvrVnodeHooksHashTable::CreateStaticTableWithSize() failed\n" ) ); goto __exit_on_error; } // // gSuperUserContext must have a valid thread and process pointer // TO DO redesign this! Indefinit holding a thread or task object is a bad behaviour. // thread_reference( current_thread() ); task_reference( current_task() ); gSuperUserContext = vfs_context_create(NULL); // vfs_context_kernel() // // create an object for the vnodes KAuth callback and register the callback, // the callback might be called immediatelly just after registration! // gVnodeGate = QvrIOKitKAuthVnodeGate::withCallbackRegistration( this ); assert( NULL != gVnodeGate ); if( NULL == gVnodeGate ){ DBG_PRINT_ERROR( ( "QvrIOKitKAuthVnodeGate::withDefaultSettings() failed\n" ) ); goto __exit_on_error; } Instance = this; // // register with IOKit to allow the class matching // registerService(); return true; __exit_on_error: // // all cleanup will be done in stop() and free() // this->release(); return false; }
IOReturn DldIPCUserClient::ipcRequest( __in void *vInBuffer, __out void *vOutBuffer, __in void *vInSize, __in void *vOutSizeP, void *, void *) { IOReturn RC = kIOReturnSuccess; DldIpcRequest* ipcRequest = (DldIpcRequest*)vInBuffer; vm_size_t inSize = (vm_size_t)vInSize; if( inSize < sizeof( *ipcRequest ) || ipcRequest->size < sizeof( *ipcRequest ) || inSize < ipcRequest->size ) return kIOReturnBadArgument; // // get a service's client to send a notification to // DldIOUserClient* serviceUserClient = gServiceUserClient.getUserClient(); if( ! serviceUserClient ) return kIOReturnOffline; SInt32 eventID = gKerneToUserEvents.getNextEventNumber(); UInt32 waitBlockIndex; if( ! this->acquireWaitBlock( &waitBlockIndex, eventID ) ){ gServiceUserClient.releaseUserClient(); return kIOReturnNoResources; } DldDriverEventData eventTemplate; bzero( &eventTemplate, sizeof( eventTemplate ) ); eventTemplate.Header.id = eventID; eventTemplate.Header.size = sizeof( eventTemplate ); eventTemplate.Header.trustedClient = this->trustedClient; eventTemplate.Header.waitBlockIndex = waitBlockIndex; // // send a notification to the service // switch( ipcRequest->operation ){ case DLD_IPCOP_STOP_SERVICE: { DldDriverEventData event; event = eventTemplate; event.Header.type = DLD_EVENT_FORCED_STOP; RC = serviceUserClient->eventNotification( &event ); break; } case DLD_IPCOP_IMPORT_DLS: { // // check the request consistency // if( ipcRequest->Tail.ImportDLS.filePathLength + sizeof( *ipcRequest ) > ipcRequest->size ){ DBG_PRINT_ERROR(("ipcRequest->Tail.ImportDLS.filePathLength + sizeof( *ipcRequest ) > ipcRequest->size," "ipcRequest->Tail.ImportDLS.filePathLength is %d," "sizeof( *ipcRequest ) is %d," "ipcRequest->size is %d\n", ipcRequest->Tail.ImportDLS.filePathLength, sizeof( *ipcRequest ), ipcRequest->size )); RC = kIOReturnBadArgument; break; } if( ipcRequest->Tail.ImportDLS.filePathLength < sizeof("/X") ){ DBG_PRINT_ERROR(("the DLS file path is too short to be valid\n")); RC = kIOReturnBadArgument; break; } // // check that the zero terminator is present // if( '\0' != ((const char*)( ipcRequest+1 ))[ ipcRequest->Tail.ImportDLS.filePathLength - 1 ] ){ DBG_PRINT_ERROR(("the DLS file path is not zero terminated\n")); RC = kIOReturnBadArgument; break; } // // check that this is a fully qualified path // if( '/' != ((const char*)( ipcRequest+1 ))[ 0 ] ){ DBG_PRINT_ERROR(("the DLS file path is not a fully qualified one\n")); RC = kIOReturnBadArgument; break; } vm_size_t sizeOfEvent = sizeof( DldDriverEventData ) + ipcRequest->Tail.ImportDLS.filePathLength; DldDriverEventData* event = (DldDriverEventData*)IOMalloc( sizeOfEvent ); assert( event ); if( !event ){ DBG_PRINT_ERROR(("allocating memory for an event failed\n")); RC = kIOReturnNoMemory; break; } bcopy( &eventTemplate, event, min( sizeof(eventTemplate), sizeOfEvent ) ); event->Header.size = sizeOfEvent; event->Header.type = DLD_EVENT_IMPORT_DLS; event->Tail.ImportDLS.adminRequest = ipcRequest->Tail.ImportDLS.adminRequest; event->Tail.ImportDLS.filePathLength = ipcRequest->Tail.ImportDLS.filePathLength; event->Tail.ImportDLS.senderUid = fClientUID; bcopy( (const void*)(ipcRequest+1), (void*)(event+1), ipcRequest->Tail.ImportDLS.filePathLength ); RC = serviceUserClient->eventNotification( event ); IOFree( event, sizeOfEvent ); event = NULL; break; } case DLD_IPCOP_GPUPDATE: { DldDriverEventData event; event = eventTemplate; event.Header.type = DLD_EVENT_GPUPDATE; RC = serviceUserClient->eventNotification( &event ); break; } default: RC = kIOReturnBadArgument; break; } // end switch gServiceUserClient.releaseUserClient(); serviceUserClient = NULL; if( kIOReturnSuccess == RC ){ RC = this->waitForCompletion( waitBlockIndex ); } else { this->releaseWaitBlock( waitBlockIndex ); } return RC; }
/*--------------------------------------------------------------------------- * HfRfCallback() *--------------------------------------------------------------------------- * * Synopsis: RFCOMM callback for the HF state machine. * * Return: void */ void ChnRfCallback(RfChannel *rfChannel, RfCallbackParms *Parms) { ChnChannel *Channel; /*AtCommands atParms;*/ U8 event = 0; U16 offset = 0; BtStatus status = BT_STATUS_SUCCESS; Report(("[CHN][ChnRfCallback] +rfChannel==x%X, RfEvent==0x%X", (U32)rfChannel, Parms->event) ); Channel = ChnFindRegisteredChannel(rfChannel); Assert(Channel != 0); switch (Parms->event) { case RFEVENT_OPEN_IND: DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM Event : RFEVENT_OPEN_IND.")); if(Channel) { Channel->remDev = Parms->ptrs.remDev; if(ChnGetSubstate(Channel) == CHN_SUBSTATE_CLOSED1) { ChnChangeSubstate(Channel, CHN_SUBSTATE_CONN_IN1); } else { Report(("ChnRfCallback: Only in C1, the server channel is registered.")); /* Only in C1, the server channel is registered, so there shall no open ind received in other substates other than C1. */ (void)RF_RejectChannel(rfChannel); } } else { Report(("ChnRfCallback: RFEVENT_OPEN_IND: HFP shall never get into this case.")); /* HFP shall never get into this case */ DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) ); (void)RF_RejectChannel(rfChannel); } break; case RFEVENT_OPEN: DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM Event : RFEVENT_OPEN.")); /* Register a SCO handler */ if (Channel) { Channel->remDev = Parms->ptrs.remDev; //CMGR_SetDeviceAsMaster(&(Channel->cmgrHandler)); //HfgEnableSniffTimerMode(&(Channel->cmgrHandler)); Channel->linkFlags |= CHN_LINK_HANDSFREE; Report(("ChnRfCallback: RFCOMM HF connection established.")); switch(ChnGetSubstate(Channel)) { case CHN_SUBSTATE_CONN_OUT3: case CHN_SUBSTATE_CONN_IN2: // Direct change state to SLC1 ChnChangeSubstate(Channel, CHN_SUBSTATE_SLC1); break; case CHN_SUBSTATE_DISC1: ChnDisconnecting(Channel); break; default: Report(("ChnRfCallback: RFEVENT_OPEN:CHN shall not get into this case.")); /* CHN shall not get into this case. */ DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) ); Assert(0); RF_CloseChannel(rfChannel); break; } } else { RF_CloseChannel(rfChannel); } break; case RFEVENT_DATA_IND: DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM Event : RFEVENT_DATA_IND.")); if (Channel) { RfBuffer rfBuf; rfBuf.len = Parms->dataLen; rfBuf.buf = Parms->ptrs.data; Report(("ChnRfCallback: RFCOMM HF data received.")); RF_AdvanceCredit(rfChannel, 1); ChnAppCallback(Channel, CHN_EVENT_RX_DATA, Parms->status, (U32)&rfBuf); } break; case RFEVENT_PACKET_HANDLED: DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM Event : RFEVENT_PACKET_HANDLED, Status=%d", Parms->status)); if (Channel) { Channel->bTxInProgress = FALSE; status = Parms->status; if(status != BT_STATUS_SUCCESS) { /* Only when RFCOMM channel is closed, it does not return success */ DBG_PRINT_AT(("[CHN][AT] RFEVENT_PACKET_HANDLED : Fail")); } else { if(ChnRemainTxData(Channel) > 0) { DBG_PRINT_AT(("[CHN][AT] Bytestosend=%d", ChnRemainTxData(Channel) )); status = ChnSendTxBuffer(Channel); if (status == BT_STATUS_PENDING) { DBG_PRINT_AT(("[CHN] ChnSendTxBuffer return Pending")); } } } } else { Report(("ChnRfCallback: RFEVENT_PACKET_HANDLED: No CHN channel.")); return; } break; case RFEVENT_CLOSED: DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM Event : RFEVENT_CLOSED")); if (Channel) { //HfgDisableSniffTimerMode(&(Channel->cmgrHandler)); Channel->linkFlags &= ~CHN_LINK_HANDSFREE; switch(ChnGetSubstate(Channel)) { case CHN_SUBSTATE_CONN_OUT3: case CHN_SUBSTATE_CONN_IN1: case CHN_SUBSTATE_CONN_IN2: case CHN_SUBSTATE_OPEN1: ChnChangeSubstate(Channel, CHN_SUBSTATE_CLOSED1); break; case CHN_SUBSTATE_OPEN2: case CHN_SUBSTATE_SLC1: case CHN_SUBSTATE_SLC2: case CHN_SUBSTATE_DISC1: ChnDisconnecting(Channel); break; default: DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) ); Assert(0); break; } } break; default: DBG_PRINT_EVENT(("[CHN][EVENT] RFCOMM ignore other events : %d", Parms->event)); /* Ignore other events */ break; } Report(("- ChnRfCallback.")); }
/*--------------------------------------------------------------------------- * ChnServiceConnectionCallback() *--------------------------------------------------------------------------- * * Synopsis: Called by device manager with link state events. * * Return: (See header file) * */ void ChnServiceConnectionCallback(CmgrHandler *Handler, CmgrEvent Event, BtStatus Status) { ChnChannel *Channel = ContainingRecord(Handler, ChnChannel, cmgrHandler); BtStatus return_code; Report(("+ ChnServiceConnectionCallback: Event=%d, Status=%d.", Event, Status)); switch (Event) { case CMEVENT_DATA_LINK_CON_CNF: DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_DATA_LINK_CON_CNF, Status=%d.", Status)); if(ChnGetSubstate(Channel) == CHN_SUBSTATE_CONN_OUT1) { if (Status == BT_STATUS_SUCCESS) { Channel->linkFlags |= CHN_LINK_ACL; /* FLOW : Start SDP query */ if ((return_code = ChnStartServiceQuery(Channel, BSQM_FIRST)) == BT_STATUS_PENDING) { ChnChangeSubstate(Channel, CHN_SUBSTATE_CONN_OUT2); } else { ChnChangeSubstate(Channel, CHN_SUBSTATE_CLOSED1); } } else { /* This event shall not be received in other substates other than C1 */ ChnChangeSubstate(Channel, CHN_SUBSTATE_CLOSED1); } } else if(ChnGetSubstate(Channel) == CHN_SUBSTATE_DISC1) { ChnDisconnecting(Channel); } else { DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) ); ChnChangeSubstate(Channel, CHN_SUBSTATE_CLOSED1); } break; case CMEVENT_DATA_LINK_DIS: /* We shall never run into this case */ DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_DATA_LINK_DIS, Status=%d.", Status)); DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) ); ChnCloseChannel(Channel); break; case CMEVENT_AUDIO_LINK_CON: DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_AUDIO_LINK_CON, Status=%d.", Status)); /* Clear SCO connecting flag */ Channel->linkFlags &= ~CHN_LINK_SCO_CONNECTING; ChnAppCallback(Channel, CHN_EVENT_AUDIO_CONNECTED, Status, (U32)&Channel->cmgrHandler.bdc->link->bdAddr); if(ChnGetSubstate(Channel) == CHN_SUBSTATE_SLC1) { /* Remote connect SCO link */ /* It could happen in SLC1 but no action needed */ } else if(ChnGetSubstate(Channel) == CHN_SUBSTATE_SLC2) { ChnChangeSubstate(Channel, CHN_SUBSTATE_SLC1); } else if(ChnGetSubstate(Channel) == CHN_SUBSTATE_DISC1) { ChnDisconnecting(Channel); } else { DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) ); } break; case CMEVENT_AUDIO_LINK_DIS: DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_AUDIO_LINK_DIS, Status=%d.", Status)); /* Clear SCO disconnecting flag */ Channel->linkFlags &= ~CHN_LINK_SCO_DISCONNECTING; ChnAppCallback(Channel, CHN_EVENT_AUDIO_DISCONNECTED, Status, (U32)&Channel->cmgrHandler.bdc->link->bdAddr); if(ChnGetSubstate(Channel) == CHN_SUBSTATE_SLC1) { /* Remote disconnect SCO link */ /* It could happen in SLC1 but no action needed */ } else if(ChnGetSubstate(Channel) == CHN_SUBSTATE_SLC2) { ChnChangeSubstate(Channel, CHN_SUBSTATE_SLC1); } else if(ChnGetSubstate(Channel) == CHN_SUBSTATE_DISC1) { ChnDisconnecting(Channel); } else { DBG_PRINT_ERROR( ("[CHN][ERR] Unexpected substate==0x%X - (%s,%d)", ChnGetSubstate(Channel), __FILE__, __LINE__) ); } break; #if BT_SCO_HCI_DATA == XA_ENABLED case CMEVENT_AUDIO_DATA: DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_AUDIO_DATA, Status=%d.", Status)); ChnAppCallback(Channel, CHN_EVENT_AUDIO_DATA, Status, (U32)&Handler->audioData); break; case CMEVENT_AUDIO_DATA_SENT: DBG_PRINT_EVENT(("[CHN][EVENT] CMGR Event : CMEVENT_AUDIO_DATA_SENT, Status=%d.", Status)); ChnAppCallback(Channel, CHN_EVENT_AUDIO_DATA_SENT, Status, (U32)&Handler->audioPacket); break; #endif } Report(("- ChnServiceConnectionCallback.")); }