1/* 2 * Copyright (c) 2014 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 "authorize.h" 25 26#include "application.h" 27#include "device.h" 28#include "preferences.h" 29 30#include <CoreFoundation/CFBundlePriv.h> 31#include <IOKit/IOKitLibPrivate.h> 32 33static IOReturn __Authorize( CFDictionaryRef deviceID, pid_t processID ) 34{ 35 CFStringRef device; 36 IOReturn status; 37 38 device = _DeviceCopyName( deviceID ); 39 40 if ( device ) 41 { 42 CFBundleRef bundle; 43 44 bundle = _ApplicationCopyBundle( processID ); 45 46 if ( bundle ) 47 { 48 CFStringRef application; 49 50 application = CFBundleGetValueForInfoDictionaryKey( bundle, _kCFBundleDisplayNameKey ); 51 52 if ( application == 0 ) 53 { 54 application = CFBundleGetValueForInfoDictionaryKey( bundle, kCFBundleNameKey ); 55 } 56 57 if ( application ) 58 { 59 CFMutableDictionaryRef dictionary; 60 61 dictionary = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); 62 63 if ( dictionary ) 64 { 65 CFStringRef string; 66 CFStringRef header; 67 68 string = CFCopyLocalizedString( CFSTR( "%@ wants to access \"%@\"." ), 0 ); 69 header = CFStringCreateWithFormat( kCFAllocatorDefault, 0, string, application, device ); 70 CFRelease( string ); 71 72 if ( header ) 73 { 74 CFURLRef path; 75 76 path = CFBundleCopyBundleURL( CFBundleGetMainBundle( ) ); 77 78 if ( path ) 79 { 80 CFUserNotificationRef notification; 81 82 CFDictionarySetValue( dictionary, kCFUserNotificationAlertHeaderKey, header ); 83 CFDictionarySetValue( dictionary, kCFUserNotificationAlertMessageKey, CFSTR( "Do you want to allow access to this device?" ) ); 84 CFDictionarySetValue( dictionary, kCFUserNotificationAlternateButtonTitleKey, CFSTR( "Always Allow" ) ); 85 CFDictionarySetValue( dictionary, kCFUserNotificationDefaultButtonTitleKey, CFSTR( "Allow" ) ); 86 CFDictionarySetValue( dictionary, kCFUserNotificationLocalizationURLKey, path ); 87 CFDictionarySetValue( dictionary, kCFUserNotificationOtherButtonTitleKey, CFSTR( "Deny" ) ); 88 89 notification = CFUserNotificationCreate( kCFAllocatorDefault, 0, kCFUserNotificationCautionAlertLevel, 0, dictionary ); 90 91 if ( notification ) 92 { 93 int err; 94 CFOptionFlags response; 95 96 err = CFUserNotificationReceiveResponse( notification, 0, &response ); 97 98 if ( err == 0 ) 99 { 100 switch ( ( response & 0x3 ) ) 101 { 102 case kCFUserNotificationAlternateResponse: 103 { 104 status = kIOReturnNotFound; 105 106 break; 107 } 108 109 case kCFUserNotificationDefaultResponse: 110 { 111 status = kIOReturnSuccess; 112 113 break; 114 } 115 116 default: 117 { 118 status = kIOReturnNotPermitted; 119 120 break; 121 } 122 } 123 } 124 else 125 { 126 status = kIOReturnNoResources; 127 } 128 129 CFRelease( notification ); 130 } 131 else 132 { 133 status = kIOReturnNoResources; 134 } 135 136 CFRelease( path ); 137 } 138 else 139 { 140 status = kIOReturnNoMemory; 141 } 142 143 CFRelease( header ); 144 } 145 else 146 { 147 status = kIOReturnNoMemory; 148 } 149 150 CFRelease( dictionary ); 151 } 152 else 153 { 154 status = kIOReturnNoMemory; 155 } 156 } 157 else 158 { 159 status = kIOReturnAborted; 160 } 161 162 CFRelease( bundle ); 163 } 164 else 165 { 166 status = kIOReturnAborted; 167 } 168 169 CFRelease( device ); 170 } 171 else 172 { 173 status = kIOReturnUnsupported; 174 } 175 176 return status; 177} 178 179IOReturn _Authorize( io_service_t service, uint64_t options, pid_t processID, uint64_t authorizationID ) 180{ 181 CFDictionaryRef deviceID; 182 IOReturn status; 183 184 deviceID = _DeviceCopyIdentifier( service ); 185 186 if ( deviceID ) 187 { 188 CFStringRef applicationID; 189 190 applicationID = _ApplicationCopyIdentifier( processID ); 191 192 if ( applicationID ) 193 { 194 CFArrayRef deviceIDs; 195 196 status = kIOReturnNotFound; 197 198 deviceIDs = _PreferencesCopyValue( applicationID ); 199 200 if ( deviceIDs ) 201 { 202 CFIndex count; 203 CFIndex index; 204 205 count = CFArrayGetCount( deviceIDs ); 206 207 for ( index = 0; index < count; index++ ) 208 { 209 CFDictionaryRef compare; 210 211 compare = ( void * ) CFArrayGetValueAtIndex( deviceIDs, index ); 212 213 if ( _DeviceIsEqual( deviceID, compare ) ) 214 { 215 status = kIOReturnSuccess; 216 217 break; 218 } 219 } 220 } 221 222 if ( status ) 223 { 224 if ( ( options & kIOServiceInteractionAllowed ) ) 225 { 226 status = __Authorize( deviceID, processID ); 227 228 if ( status == kIOReturnNotFound ) 229 { 230 _PreferencesAppendArrayValue( applicationID, deviceID ); 231 232 status = kIOReturnSuccess; 233 } 234 } 235 else 236 { 237 status = kIOReturnNotPermitted; 238 } 239 } 240 241 if ( status == kIOReturnSuccess ) 242 { 243 status = _IOServiceSetAuthorizationID( service, authorizationID ); 244 } 245 246 if ( deviceIDs ) 247 { 248 CFRelease( deviceIDs ); 249 } 250 251 CFRelease( applicationID ); 252 } 253 else 254 { 255 status = kIOReturnAborted; 256 } 257 258 CFRelease( deviceID ); 259 } 260 else 261 { 262 status = kIOReturnUnsupported; 263 } 264 265 return status; 266} 267