1/* Copyright (c) 2012-2013 Apple Inc. All Rights Reserved. */ 2 3#include "authtoken.h" 4#include "authd_private.h" 5#include "process.h" 6#include "authitems.h" 7#include "debugging.h" 8#include "authutilities.h" 9#include "server.h" 10 11#include <CommonCrypto/CommonRandomSPI.h> 12#include <Security/Authorization.h> 13#include <Security/SecBase.h> 14#include <sandbox.h> 15 16static Boolean AuthTokenEqualCallBack(const void *value1, const void *value2) 17{ 18 return (*(uint64_t*)value1) == (*(uint64_t*)value2); 19} 20 21static CFHashCode AuthTokenHashCallBack(const void *value) 22{ 23// CFHashCode hash; 24// AuthorizationBlob* blob = (AuthorizationBlob*)value; 25// hash = blob->data[1]; 26// hash <<= 32; 27// hash |= blob->data[0]; 28// return hash; 29 //quick 64 bit aligned version 30 return *((CFHashCode*)((AuthorizationBlob*)value)->data); 31} 32 33const CFDictionaryKeyCallBacks kAuthTokenKeyCallBacks = { 34 .version = 0, 35 .retain = NULL, 36 .release = NULL, 37 .copyDescription = NULL, 38 .equal = &AuthTokenEqualCallBack, 39 .hash = &AuthTokenHashCallBack 40}; 41 42struct _auth_token_s { 43 __AUTH_BASE_STRUCT_HEADER__; 44 45 AuthorizationBlob blob; 46 auth_token_state_t state; 47 audit_info_s auditInfo; 48 dispatch_queue_t dispatch_queue; 49 50 CFMutableSetRef processes; 51 52 session_t session; 53 process_t creator; // weak reference, used for entitlement checking 54 mach_port_t creator_bootstrap_port; 55 56 auth_items_t context; 57 58 CFMutableSetRef credentials; 59 CFMutableSetRef authorized_rights; 60 61 bool least_privileged; 62 bool appleSigned; 63 64 bool sandboxed; 65 char * code_url; 66 67 credential_t credential; 68}; 69 70static void 71_auth_token_finalize(CFTypeRef value) 72{ 73 auth_token_t auth = (auth_token_t)value; 74 LOGV("authtoken: deallocated %p", auth); 75 76 dispatch_barrier_sync(auth->dispatch_queue, ^{}); 77 78 dispatch_release(auth->dispatch_queue); 79 CFReleaseSafe(auth->session); 80 CFReleaseSafe(auth->processes); 81 CFReleaseSafe(auth->context); 82 CFReleaseSafe(auth->credentials); 83 CFReleaseSafe(auth->authorized_rights); 84 free_safe(auth->code_url); 85 CFReleaseSafe(auth->credential); 86 87 if (auth->creator_bootstrap_port != MACH_PORT_NULL) { 88 mach_port_deallocate(mach_task_self(), auth->creator_bootstrap_port); 89 } 90} 91 92static Boolean 93_auth_token_equal(CFTypeRef value1, CFTypeRef value2) 94{ 95 auth_token_t auth1 = (auth_token_t)value1; 96 auth_token_t auth2 = (auth_token_t)value2; 97 98 return memcmp(&auth1->blob, &auth2->blob, sizeof(AuthorizationBlob)) == 0; 99} 100 101static CFStringRef 102_auth_token_copy_description(CFTypeRef value) 103{ 104 auth_token_t auth = (auth_token_t)value; 105 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("auth_token: %p, uid=%i, pid=%i, processes=%li least_privileged=%i"), 106 auth, auth->auditInfo.euid, auth->auditInfo.pid, CFSetGetCount(auth->processes), auth->least_privileged); 107} 108 109static CFHashCode 110_auth_token_hash(CFTypeRef value) 111{ 112 auth_token_t auth = (auth_token_t)value; 113 return *(CFHashCode*)&auth->blob; 114} 115 116AUTH_TYPE_INSTANCE(auth_token, 117 .init = NULL, 118 .copy = NULL, 119 .finalize = _auth_token_finalize, 120 .equal = _auth_token_equal, 121 .hash = _auth_token_hash, 122 .copyFormattingDesc = NULL, 123 .copyDebugDesc = _auth_token_copy_description 124 ); 125 126static CFTypeID auth_token_get_type_id() { 127 static CFTypeID type_id = _kCFRuntimeNotATypeID; 128 static dispatch_once_t onceToken; 129 130 dispatch_once(&onceToken, ^{ 131 type_id = _CFRuntimeRegisterClass(&_auth_type_auth_token); 132 }); 133 134 return type_id; 135} 136 137static auth_token_t 138_auth_token_create(const audit_info_s * auditInfo, bool operateAsLeastPrivileged) 139{ 140#if __LLP64__ 141 __Check_Compile_Time(sizeof(CFHashCode) == sizeof(AuthorizationBlob)); 142#endif 143 144 auth_token_t auth = (auth_token_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, auth_token_get_type_id(), AUTH_CLASS_SIZE(auth_token), NULL); 145 require(auth != NULL, done); 146 147 if (CCRandomCopyBytes(kCCRandomDefault, auth->blob.data, sizeof(auth->blob.data)) != kCCSuccess) { 148 LOGE("authtoken[%i]: failed to generate blob", auditInfo->pid); 149 CFReleaseNull(auth); 150 goto done; 151 } 152 153 auth->context = auth_items_create(); 154 auth->auditInfo = *auditInfo; 155 auth->least_privileged = operateAsLeastPrivileged; 156 157 auth->dispatch_queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 158 check(auth->dispatch_queue != NULL); 159 160 auth->credentials = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks); 161 auth->authorized_rights = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks); 162 auth->processes = CFSetCreateMutable(kCFAllocatorDefault, 0, NULL); 163 auth->creator_bootstrap_port = MACH_PORT_NULL; 164 165 if (sandbox_check(auth->auditInfo.pid, "authorization-right-obtain", SANDBOX_CHECK_NO_REPORT) != 0) 166 auth->sandboxed = true; 167 else 168 auth->sandboxed = false; 169 170#if DEBUG 171 CFHashCode code = AuthTokenHashCallBack(&auth->blob); 172 if (memcmp(&code, auth->blob.data, sizeof(auth->blob.data)) != 0) { 173 LOGD("authtoken[%i]: blob = %x%01x", auth->auditInfo.pid, auth->blob.data[1], auth->blob.data[0]); 174 LOGD("authtoken[%i]: hash = %lx", auth->auditInfo.pid, code); 175 assert(false); 176 } 177#endif 178 179done: 180 return auth; 181} 182 183auth_token_t 184auth_token_create(process_t proc, bool operateAsLeastPrivileged) 185{ 186 auth_token_t auth = NULL; 187 require(proc != NULL, done); 188 189 auth = _auth_token_create(process_get_audit_info(proc), operateAsLeastPrivileged); 190 require(auth != NULL, done); 191 192 auth->creator = proc; 193 auth->session = (session_t)CFRetain(process_get_session(proc)); 194 auth->code_url = _copy_string(process_get_code_url(proc)); 195 auth->appleSigned = process_apple_signed(proc); 196 auth->creator_bootstrap_port = process_get_bootstrap(proc); 197 // This line grabs a reference to the send right to the bootstrap (our right to send to the bootstrap) 198 // This makes it critical to use the same call in reverse as we are only getting a ref to one right, 199 // but deallocate will free a ref to all 5 rights. 200 if (auth->creator_bootstrap_port != MACH_PORT_NULL) { 201 kern_return_t error_code = mach_port_mod_refs(mach_task_self(), auth->creator_bootstrap_port, MACH_PORT_RIGHT_SEND, 1); 202 if (error_code != KERN_SUCCESS) { 203 // If no reference to the mach port right can be obtained, we don't hold the copy, so mark it NULL again! 204 auth->creator_bootstrap_port = MACH_PORT_NULL; 205 } 206 } 207 208 LOGV("authtoken[%i]: created %p", auth->auditInfo.pid, auth); 209 210done: 211 return auth; 212} 213 214auth_token_t 215auth_token_create_with_audit_info(const audit_info_s* info, bool operateAsLeastPrivileged) 216{ 217 OSStatus status = errSecSuccess; 218 SecCodeRef code_Ref = NULL; 219 CFURLRef code_url = NULL; 220 221 auth_token_t auth = NULL; 222 require(info != NULL, done); 223 224 auth = _auth_token_create(info, operateAsLeastPrivileged); 225 require(auth != NULL, done); 226 227 auth->session = server_find_copy_session(info->asid, true); 228 if (auth->session == NULL) { 229 LOGV("authtoken[%i]: failed to create session", auth->auditInfo.pid); 230 CFReleaseNull(auth); 231 goto done; 232 } 233 234 CFMutableDictionaryRef codeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 235 CFNumberRef codePid = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &auth->auditInfo.pid); 236 CFDictionarySetValue(codeDict, kSecGuestAttributePid, codePid); 237 status = SecCodeCopyGuestWithAttributes(NULL, codeDict, kSecCSDefaultFlags, &code_Ref); 238 CFReleaseSafe(codeDict); 239 CFReleaseSafe(codePid); 240 241 if (status) { 242 LOGV("authtoken[%i]: failed to create code ref (%i)", auth->auditInfo.pid, status); 243 CFReleaseNull(auth); 244 goto done; 245 } 246 247 if (SecCodeCopyPath(code_Ref, kSecCSDefaultFlags, &code_url) == errSecSuccess) { 248 auth->code_url = calloc(1u, PATH_MAX+1); 249 if (auth->code_url) { 250 CFURLGetFileSystemRepresentation(code_url, true, (UInt8*)auth->code_url, PATH_MAX); 251 } 252 } 253 254 LOGV("authtoken[%i]: created %p for %s", auth->auditInfo.pid, auth, auth->code_url); 255 256done: 257 CFReleaseSafe(code_Ref); 258 CFReleaseSafe(code_url); 259 return auth; 260} 261 262bool 263auth_token_get_sandboxed(auth_token_t auth) 264{ 265 return auth->sandboxed; 266} 267 268const char * 269auth_token_get_code_url(auth_token_t auth) 270{ 271 return auth->code_url; 272} 273 274const void * 275auth_token_get_key(auth_token_t auth) 276{ 277 return &auth->blob; 278} 279 280auth_items_t 281auth_token_get_context(auth_token_t auth) 282{ 283 return auth->context; 284} 285 286bool 287auth_token_least_privileged(auth_token_t auth) 288{ 289 return auth->least_privileged; 290} 291 292uid_t 293auth_token_get_uid(auth_token_t auth) 294{ 295 return auth ? auth->auditInfo.euid : (uid_t)-2; 296} 297 298pid_t 299auth_token_get_pid(auth_token_t auth) 300{ 301 return auth ? auth->auditInfo.pid : -1; 302} 303 304session_t 305auth_token_get_session(auth_token_t auth) 306{ 307 return auth->session; 308} 309 310const AuthorizationBlob * 311auth_token_get_blob(auth_token_t auth) 312{ 313 return &auth->blob; 314} 315 316const audit_info_s * 317auth_token_get_audit_info(auth_token_t auth) 318{ 319 return &auth->auditInfo; 320} 321 322mach_port_t 323auth_token_get_creator_bootstrap(auth_token_t auth) 324{ 325 return auth->creator_bootstrap_port; 326} 327 328CFIndex 329auth_token_add_process(auth_token_t auth, process_t proc) 330{ 331 __block CFIndex count = 0; 332 dispatch_sync(auth->dispatch_queue, ^{ 333 CFSetAddValue(auth->processes, proc); 334 count = CFSetGetCount(auth->processes); 335 }); 336 return count; 337} 338 339CFIndex 340auth_token_remove_process(auth_token_t auth, process_t proc) 341{ 342 __block CFIndex count = 0; 343 dispatch_sync(auth->dispatch_queue, ^{ 344 if (auth->creator == proc) { 345 auth->creator = NULL; 346 } 347 CFSetRemoveValue(auth->processes, proc); 348 count = CFSetGetCount(auth->processes); 349 }); 350 return count; 351} 352 353CFIndex 354auth_token_get_process_count(auth_token_t auth) 355{ 356 __block CFIndex count = 0; 357 dispatch_sync(auth->dispatch_queue, ^{ 358 count = CFSetGetCount(auth->processes); 359 }); 360 return count; 361} 362 363void 364auth_token_set_credential(auth_token_t auth, credential_t cred) 365{ 366 dispatch_sync(auth->dispatch_queue, ^{ 367 CFSetSetValue(auth->credentials, cred); 368 }); 369} 370 371bool 372auth_token_credentials_iterate(auth_token_t auth, credential_iterator_t iter) 373{ 374 __block bool result = false; 375 376 dispatch_sync(auth->dispatch_queue, ^{ 377 CFIndex count = CFSetGetCount(auth->credentials); 378 CFTypeRef values[count]; 379 CFSetGetValues(auth->credentials, values); 380 for (CFIndex i = 0; i < count; i++) { 381 credential_t cred = (credential_t)values[i]; 382 result = iter(cred); 383 if (!result) { 384 break; 385 } 386 } 387 }); 388 389 return result; 390} 391 392void 393auth_token_set_right(auth_token_t auth, credential_t right) 394{ 395 dispatch_sync(auth->dispatch_queue, ^{ 396 CFSetSetValue(auth->authorized_rights, right); 397 }); 398} 399 400bool 401auth_token_rights_iterate(auth_token_t auth, credential_iterator_t iter) 402{ 403 __block bool result = false; 404 405 dispatch_sync(auth->dispatch_queue, ^{ 406 CFIndex count = CFSetGetCount(auth->authorized_rights); 407 CFTypeRef values[count]; 408 CFSetGetValues(auth->authorized_rights, values); 409 for (CFIndex i = 0; i < count; i++) { 410 credential_t right = (credential_t)values[i]; 411 result = iter(right); 412 if (!result) { 413 break; 414 } 415 } 416 }); 417 418 return result; 419} 420 421CFTypeRef 422auth_token_copy_entitlement_value(auth_token_t auth, const char * entitlement) 423{ 424 __block CFTypeRef value = NULL; 425 dispatch_sync(auth->dispatch_queue, ^{ 426 if (auth->creator) { 427 value = process_copy_entitlement_value(auth->creator, entitlement); 428 } 429 }); 430 431 return value; 432} 433 434bool 435auth_token_has_entitlement(auth_token_t auth, const char * entitlement) 436{ 437 __block bool entitled = false; 438 439 dispatch_sync(auth->dispatch_queue, ^{ 440 if (auth->creator) { 441 entitled = process_has_entitlement(auth->creator, entitlement); 442 } 443 }); 444 445 return entitled; 446} 447 448bool 449auth_token_has_entitlement_for_right(auth_token_t auth, const char * right) 450{ 451 __block bool entitled = false; 452 453 dispatch_sync(auth->dispatch_queue, ^{ 454 if (auth->creator) { 455 entitled = process_has_entitlement_for_right(auth->creator, right); 456 } 457 }); 458 459 return entitled; 460} 461 462credential_t 463auth_token_get_credential(auth_token_t auth) 464{ 465 dispatch_sync(auth->dispatch_queue, ^{ 466 if (auth->credential == NULL) { 467 auth->credential = credential_create(auth->auditInfo.euid); 468 } 469 }); 470 471 return auth->credential; 472} 473 474bool 475auth_token_apple_signed(auth_token_t auth) 476{ 477 return auth->appleSigned; 478} 479 480bool auth_token_is_creator(auth_token_t auth, process_t proc) 481{ 482 __block bool creator = false; 483 if (proc) { 484 dispatch_sync(auth->dispatch_queue, ^{ 485 if (auth->creator == proc) { 486 creator = true; 487 } 488 }); 489 } 490 return creator; 491} 492 493void auth_token_set_state(auth_token_t auth, auth_token_state_t state) 494{ 495 auth->state |= state; 496} 497 498void auth_token_clear_state(auth_token_t auth, auth_token_state_t state) 499{ 500 auth->state &= ~state; 501} 502 503auth_token_state_t auth_token_get_state(auth_token_t auth) 504{ 505 return auth->state; 506} 507 508bool auth_token_check_state(auth_token_t auth, auth_token_state_t state) 509{ 510 if (state) { 511 return (auth->state & state) != 0; 512 } else { 513 return auth->state == 0; 514 } 515} 516