1/* Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. */ 2 3#include "engine.h" 4#include "rule.h" 5#include "authitems.h" 6#include "authtoken.h" 7#include "agent.h" 8#include "process.h" 9#include "debugging.h" 10#include "server.h" 11#include "credential.h" 12#include "session.h" 13#include "mechanism.h" 14#include "authutilities.h" 15#include "ccaudit.h" 16#include "connection.h" 17 18#include <pwd.h> 19#include <Security/checkpw.h> 20int checkpw_internal( const struct passwd *pw, const char* password ); 21 22#include <Security/AuthorizationTags.h> 23#include <Security/AuthorizationTagsPriv.h> 24#include <Security/AuthorizationPlugin.h> 25#include <sandbox.h> 26 27static void _set_process_hints(auth_items_t, process_t); 28static void _set_process_immutable_hints(auth_items_t, process_t); 29static void _set_auth_token_hints(auth_items_t, auth_token_t); 30static OSStatus _evaluate_user_credential_for_rule(engine_t, credential_t, rule_t, bool, bool, enum Reason *); 31static void _engine_set_credential(engine_t, credential_t, bool); 32static OSStatus _evaluate_rule(engine_t, rule_t, bool *); 33 34enum { 35 kEngineHintsFlagTemporary = (1 << 30) 36}; 37 38#pragma mark - 39#pragma mark engine creation 40 41struct _engine_s { 42 __AUTH_BASE_STRUCT_HEADER__; 43 44 connection_t conn; 45 process_t proc; 46 auth_token_t auth; 47 48 AuthorizationFlags flags; 49 auth_items_t hints; 50 auth_items_t context; 51 auth_items_t sticky_context; 52 auth_items_t immutable_hints; 53 54 auth_rights_t grantedRights; 55 56 enum Reason reason; 57 int32_t tries; 58 59 CFAbsoluteTime now; 60 61 credential_t sessionCredential; 62 CFMutableSetRef credentials; 63 CFMutableSetRef effectiveCredentials; 64 65 CFMutableDictionaryRef mechanism_agents; 66 67 // set only in engine_authorize 68 const char * currentRightName; // weak ref 69 rule_t currentRule; // weak ref 70 71 rule_t authenticateRule; 72 73 bool dismissed; 74}; 75 76static void 77_engine_finalizer(CFTypeRef value) 78{ 79 engine_t engine = (engine_t)value; 80 81 CFReleaseSafe(engine->mechanism_agents); 82 CFReleaseSafe(engine->conn); 83 CFReleaseSafe(engine->auth); 84 CFReleaseSafe(engine->hints); 85 CFReleaseSafe(engine->context); 86 CFReleaseSafe(engine->immutable_hints); 87 CFReleaseSafe(engine->sticky_context); 88 CFReleaseSafe(engine->grantedRights); 89 CFReleaseSafe(engine->sessionCredential); 90 CFReleaseSafe(engine->credentials); 91 CFReleaseSafe(engine->effectiveCredentials); 92 CFReleaseSafe(engine->authenticateRule); 93} 94 95AUTH_TYPE_INSTANCE(engine, 96 .init = NULL, 97 .copy = NULL, 98 .finalize = _engine_finalizer, 99 .equal = NULL, 100 .hash = NULL, 101 .copyFormattingDesc = NULL, 102 .copyDebugDesc = NULL 103 ); 104 105static CFTypeID engine_get_type_id() { 106 static CFTypeID type_id = _kCFRuntimeNotATypeID; 107 static dispatch_once_t onceToken; 108 109 dispatch_once(&onceToken, ^{ 110 type_id = _CFRuntimeRegisterClass(&_auth_type_engine); 111 }); 112 113 return type_id; 114} 115 116engine_t 117engine_create(connection_t conn, auth_token_t auth) 118{ 119 engine_t engine = NULL; 120 require(conn != NULL, done); 121 require(auth != NULL, done); 122 123 engine = (engine_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, engine_get_type_id(), AUTH_CLASS_SIZE(engine), NULL); 124 require(engine != NULL, done); 125 126 engine->conn = (connection_t)CFRetain(conn); 127 engine->proc = connection_get_process(conn); 128 engine->auth = (auth_token_t)CFRetain(auth); 129 130 engine->hints = auth_items_create(); 131 engine->context = auth_items_create(); 132 engine->immutable_hints = auth_items_create(); 133 engine->sticky_context = auth_items_create(); 134 _set_process_hints(engine->hints, engine->proc); 135 _set_process_immutable_hints(engine->immutable_hints, engine->proc); 136 _set_auth_token_hints(engine->hints, auth); 137 138 engine->grantedRights = auth_rights_create(); 139 140 engine->reason = noReason; 141 142 engine->now = CFAbsoluteTimeGetCurrent(); 143 144 session_update(auth_token_get_session(engine->auth)); 145 engine->sessionCredential = credential_create(session_get_uid(auth_token_get_session(engine->auth))); 146 engine->credentials = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks); 147 engine->effectiveCredentials = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks); 148 149 session_credentials_iterate(auth_token_get_session(engine->auth), ^bool(credential_t cred) { 150 CFSetAddValue(engine->effectiveCredentials, cred); 151 return true; 152 }); 153 154 auth_token_credentials_iterate(engine->auth, ^bool(credential_t cred) { 155 // we added all session credentials already now just add all previously acquired credentials 156 if (!credential_get_shared(cred)) { 157 CFSetAddValue(engine->credentials, cred); 158 } 159 return true; 160 }); 161 162 engine->mechanism_agents = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 163 164done: 165 return engine; 166} 167 168#pragma mark - 169#pragma mark agent hints 170 171void 172_set_process_hints(auth_items_t hints, process_t proc) 173{ 174 // process information 175 RequestorType type = bundle; 176 auth_items_set_data(hints, AGENT_HINT_CLIENT_TYPE, &type, sizeof(type)); 177 auth_items_set_int(hints, AGENT_HINT_CLIENT_PID, process_get_pid(proc)); 178 auth_items_set_uint(hints, AGENT_HINT_CLIENT_UID, process_get_uid(proc)); 179} 180 181void 182_set_process_immutable_hints(auth_items_t immutable_hints, process_t proc) 183{ 184 // process information - immutable 185 auth_items_set_bool(immutable_hints, AGENT_HINT_PROCESS_SIGNED, process_apple_signed(proc)); 186} 187 188void 189_set_auth_token_hints(auth_items_t hints, auth_token_t auth) 190{ 191 auth_items_set_string(hints, AGENT_HINT_CLIENT_PATH, auth_token_get_code_url(auth)); 192 auth_items_set_int(hints, AGENT_HINT_CREATOR_PID, auth_token_get_pid(auth)); 193 const audit_info_s * info = auth_token_get_audit_info(auth); 194 auth_items_set_data(hints, AGENT_HINT_CREATOR_AUDIT_TOKEN, &info->opaqueToken, sizeof(info->opaqueToken)); 195} 196 197static void 198_set_right_hints(auth_items_t hints, const char * right) 199{ 200 auth_items_set_string(hints, AGENT_HINT_AUTHORIZE_RIGHT, right); 201} 202 203static void 204_set_rule_hints(auth_items_t hints, rule_t rule) 205{ 206 auth_items_set_string(hints, AGENT_HINT_AUTHORIZE_RULE, rule_get_name(rule)); 207 const char * group = rule_get_group(rule); 208 if (rule_get_class(rule) == RC_USER && group != NULL) { 209 auth_items_set_string(hints, AGENT_HINT_REQUIRE_USER_IN_GROUP, group); 210 } else { 211 auth_items_remove(hints, AGENT_HINT_REQUIRE_USER_IN_GROUP); 212 } 213} 214 215static void 216_set_localization_hints(authdb_connection_t dbconn, auth_items_t hints, rule_t rule) 217{ 218 char * key = calloc(1u, 128); 219 220 authdb_step(dbconn, "SELECT lang,value FROM prompts WHERE r_id = ?", ^(sqlite3_stmt *stmt) { 221 sqlite3_bind_int64(stmt, 1, rule_get_id(rule)); 222 }, ^bool(auth_items_t data) { 223 snprintf(key, 128, "%s%s", kAuthorizationRuleParameterDescription, auth_items_get_string(data, "lang")); 224 auth_items_set_string(hints, key, auth_items_get_string(data, "value")); 225 auth_items_set_flags(hints, key, kEngineHintsFlagTemporary); 226 return true; 227 }); 228 229 authdb_step(dbconn, "SELECT lang,value FROM buttons WHERE r_id = ?", ^(sqlite3_stmt *stmt) { 230 sqlite3_bind_int64(stmt, 1, rule_get_id(rule)); 231 }, ^bool(auth_items_t data) { 232 snprintf(key, 128, "%s%s", kAuthorizationRuleParameterButton, auth_items_get_string(data, "lang")); 233 auth_items_set_string(hints, key, auth_items_get_string(data, "value")); 234 auth_items_set_flags(hints, key, kEngineHintsFlagTemporary); 235 return true; 236 }); 237 238 free_safe(key); 239} 240 241static void 242_set_session_hints(engine_t engine, rule_t rule) 243{ 244 LOGV("engine[%i]: ** prepare agent hints for rule %s", connection_get_pid(engine->conn), rule_get_name(rule)); 245 if (_evaluate_user_credential_for_rule(engine, engine->sessionCredential, rule, true, true, NULL) == errAuthorizationSuccess) { 246 const char * tmp = credential_get_name(engine->sessionCredential); 247 if (tmp != NULL) { 248 auth_items_set_string(engine->hints, AGENT_HINT_SUGGESTED_USER, tmp); 249 } 250 tmp = credential_get_realname(engine->sessionCredential); 251 if (tmp != NULL) { 252 auth_items_set_string(engine->hints, AGENT_HINT_SUGGESTED_USER_LONG, tmp); 253 } 254 } else { 255 auth_items_remove(engine->hints, AGENT_HINT_SUGGESTED_USER); 256 auth_items_remove(engine->hints, AGENT_HINT_SUGGESTED_USER_LONG); 257 } 258} 259 260#pragma mark - 261#pragma mark right processing 262 263static OSStatus 264_evaluate_credential_for_rule(engine_t engine, credential_t cred, rule_t rule, bool ignoreShared, bool sessionOwner, enum Reason * reason) 265{ 266 if (auth_token_least_privileged(engine->auth)) { 267 if (credential_is_right(cred) && credential_get_valid(cred) && _compare_string(engine->currentRightName, credential_get_name(cred))) { 268 if (!ignoreShared) { 269 if (!rule_get_shared(rule) && credential_get_shared(cred)) { 270 LOGV("engine[%i]: - shared right %s (does NOT satisfy rule)", connection_get_pid(engine->conn), credential_get_name(cred)); 271 if (reason) { *reason = unknownReason; } 272 return errAuthorizationDenied; 273 } 274 } 275 276 return errAuthorizationSuccess; 277 } else { 278 if (reason) { *reason = unknownReason; } 279 return errAuthorizationDenied; 280 } 281 } else { 282 return _evaluate_user_credential_for_rule(engine,cred,rule,ignoreShared,sessionOwner, reason); 283 } 284} 285 286static OSStatus 287_evaluate_user_credential_for_rule(engine_t engine, credential_t cred, rule_t rule, bool ignoreShared, bool sessionOwner, enum Reason * reason) 288{ 289 const char * cred_label = sessionOwner ? "session owner" : "credential"; 290 LOGV("engine[%i]: - validating %s%s %s (%i) for %s", connection_get_pid(engine->conn), 291 credential_get_shared(cred) ? "shared " : "", 292 cred_label, 293 credential_get_name(cred), 294 credential_get_uid(cred), 295 rule_get_name(rule)); 296 297 if (rule_get_class(rule) != RC_USER) { 298 LOGV("engine[%i]: - invalid rule class %i (denied)", connection_get_pid(engine->conn), rule_get_class(rule)); 299 return errAuthorizationDenied; 300 } 301 302 if (credential_get_valid(cred) != true) { 303 LOGV("engine[%i]: - %s %i invalid (does NOT satisfy rule)", connection_get_pid(engine->conn), cred_label, credential_get_uid(cred)); 304 if (reason) { *reason = invalidPassphrase; } 305 return errAuthorizationDenied; 306 } 307 308 if (engine->now - credential_get_creation_time(cred) > rule_get_timeout(rule)) { 309 LOGV("engine[%i]: - %s %i expired '%f > %lli' (does NOT satisfy rule)", connection_get_pid(engine->conn), cred_label, credential_get_uid(cred), 310 (engine->now - credential_get_creation_time(cred)), rule_get_timeout(rule)); 311 if (reason) { *reason = unknownReason; } 312 return errAuthorizationDenied; 313 } 314 315 316 if (!ignoreShared) { 317 if (!rule_get_shared(rule) && credential_get_shared(cred)) { 318 LOGV("engine[%i]: - shared %s %i (does NOT satisfy rule)", connection_get_pid(engine->conn), cred_label, credential_get_uid(cred)); 319 if (reason) { *reason = unknownReason; } 320 return errAuthorizationDenied; 321 } 322 } 323 324 if (credential_get_uid(cred) == 0) { 325 LOGV("engine[%i]: - %s %i has uid 0 (does satisfy rule)", connection_get_pid(engine->conn), cred_label, credential_get_uid(cred)); 326 return errAuthorizationSuccess; 327 } 328 329 if (rule_get_session_owner(rule)) { 330 if (credential_get_uid(cred) == session_get_uid(auth_token_get_session(engine->auth))) { 331 LOGV("engine[%i]: - %s %i is session owner (does satisfy rule)", connection_get_pid(engine->conn), cred_label, credential_get_uid(cred)); 332 return errAuthorizationSuccess; 333 } 334 } 335 336 if (rule_get_group(rule) != NULL) { 337 do 338 { 339 // This allows testing a group modifier without prompting the user 340 // When (authenticate-user = false) we are just testing the creator uid. 341 // If a group modifier is enabled (RuleFlagEntitledAndGroup | RuleFlagVPNEntitledAndGroup) 342 // we want to skip the creator uid group check. 343 // group modifiers are checked early during the evaluation in _check_entitlement_for_rule 344 if (!rule_get_authenticate_user(rule)) { 345 if (rule_check_flags(rule, RuleFlagEntitledAndGroup | RuleFlagVPNEntitledAndGroup)) { 346 break; 347 } 348 } 349 350 if (credential_check_membership(cred, rule_get_group(rule))) { 351 LOGV("engine[%i]: - %s %i is member of group %s (does satisfy rule)", connection_get_pid(engine->conn), cred_label, credential_get_uid(cred), rule_get_group(rule)); 352 return errAuthorizationSuccess; 353 } else { 354 if (reason) { *reason = userNotInGroup; } 355 } 356 } while (0); 357 } else if (rule_get_session_owner(rule)) { // rule asks only if user is the session owner 358 if (reason) { *reason = unacceptableUser; } 359 } 360 361 LOGV("engine[%i]: - %s %i (does NOT satisfy rule)", connection_get_pid(engine->conn), cred_label, credential_get_uid(cred)); 362 return errAuthorizationDenied; 363} 364 365static agent_t 366_get_agent(engine_t engine, mechanism_t mech, bool create, bool firstMech) 367{ 368 agent_t agent = (agent_t)CFDictionaryGetValue(engine->mechanism_agents, mech); 369 if (create && !agent) { 370 agent = agent_create(engine, mech, engine->auth, engine->proc, firstMech); 371 if (agent) { 372 CFDictionaryAddValue(engine->mechanism_agents, mech, agent); 373 CFReleaseSafe(agent); 374 } 375 } 376 return agent; 377} 378 379static uint64_t 380_evaluate_builtin_mechanism(engine_t engine, mechanism_t mech) 381{ 382 uint64_t result = kAuthorizationResultDeny; 383 384 switch (mechanism_get_type(mech)) { 385 case kMechanismTypeEntitled: 386 if (auth_token_has_entitlement_for_right(engine->auth, engine->currentRightName)) { 387 result = kAuthorizationResultAllow; 388 } 389 break; 390 default: 391 break; 392 } 393 394 return result; 395} 396 397 398static OSStatus 399_evaluate_mechanisms(engine_t engine, CFArrayRef mechanisms) 400{ 401 uint64_t result = kAuthorizationResultAllow; 402 ccaudit_t ccaudit = ccaudit_create(engine->proc, engine->auth, AUE_ssauthmech); 403 auth_items_t context = auth_items_create(); 404 auth_items_t hints = auth_items_create(); 405 406 auth_items_copy(context, engine->context); 407 auth_items_copy(hints, engine->hints); 408 auth_items_copy(context, engine->sticky_context); 409 410 CFIndex count = CFArrayGetCount(mechanisms); 411 for (CFIndex i = 0; i < count; i++) { 412 mechanism_t mech = (mechanism_t)CFArrayGetValueAtIndex(mechanisms, i); 413 414 if (mechanism_get_type(mech)) { 415 LOGV("engine[%i]: running builtin mechanism %s (%li of %li)", connection_get_pid(engine->conn), mechanism_get_string(mech), i+1, count); 416 result = _evaluate_builtin_mechanism(engine, mech); 417 } else { 418 agent_t agent = _get_agent(engine, mech, true, i == 0); 419 require_action(agent != NULL, done, result = kAuthorizationResultUndefined; LOGE("engine[%i]: error creating mechanism agent", connection_get_pid(engine->conn))); 420 421 // check if any agent has been interrupted (it necessary if interrupt will come during creation) 422 CFIndex j; 423 agent_t agent1; 424 for (j = 0; j < i; j++) { 425 agent1 = _get_agent(engine, (mechanism_t)CFArrayGetValueAtIndex(mechanisms, j), false, j == 0); 426 if(agent_get_state(agent1) == interrupting) { 427 break; 428 } 429 } 430 if (j < i) { 431 LOGV("engine[%i]: mechanisms interrupted", connection_get_pid(engine->conn)); 432 char * buf = NULL; 433 asprintf(&buf, "evaluation interrupted by %s; restarting evaluation there", mechanism_get_string(agent_get_mechanism(agent1))); 434 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(agent_get_mechanism(agent1)), kAuthorizationResultAllow, buf); 435 free_safe(buf); 436 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(mech), kAuthorizationResultAllow, NULL); 437 const char * token_name = auth_items_get_string(hints, AGENT_HINT_TOKEN_NAME); 438 if (token_name && strlen(token_name) == 0) { 439 auth_items_remove(hints, AGENT_HINT_TOKEN_NAME); 440 } 441 auth_items_copy(context, agent_get_context(agent1)); 442 auth_items_copy(hints, agent_get_hints(agent1)); 443 444 i = j - 1; 445 446 continue; 447 } 448 449 LOGV("engine[%i]: running mechanism %s (%li of %li)", connection_get_pid(engine->conn), mechanism_get_string(agent_get_mechanism(agent)), i+1, count); 450 result = agent_run(agent, hints, context, engine->immutable_hints); 451 452 auth_items_copy(context, agent_get_context(agent)); 453 auth_items_copy(hints, agent_get_hints(agent)); 454 455 bool interrupted = false; 456 for (CFIndex i2 = 0; i2 != i; i2++) { 457 agent_t agent2 = _get_agent(engine, (mechanism_t)CFArrayGetValueAtIndex(mechanisms, i2), false, i == 0); 458 if (agent_get_state(agent2) == interrupting) { 459 agent_deactivate(agent); 460 interrupted = true; 461 i = i2 - 1; 462 char * buf = NULL; 463 asprintf(&buf, "evaluation interrupted by %s; restarting evaluation there", mechanism_get_string(agent_get_mechanism(agent2))); 464 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(agent_get_mechanism(agent2)), kAuthorizationResultAllow, buf); 465 free_safe(buf); 466 auth_items_copy(context, agent_get_context(agent2)); 467 auth_items_copy(hints, agent_get_hints(agent2)); 468 break; 469 } 470 } 471 472 // Empty token name means that token doesn't exist (e.g. SC was removed). 473 // Remove empty token name from hints for UI drawing logic. 474 const char * token_name = auth_items_get_string(hints, AGENT_HINT_TOKEN_NAME); 475 if (token_name && strlen(token_name) == 0) { 476 auth_items_remove(hints, AGENT_HINT_TOKEN_NAME); 477 } 478 479 if (interrupted) { 480 LOGV("engine[%i]: mechanisms interrupted", connection_get_pid(engine->conn)); 481 enum Reason reason = worldChanged; 482 auth_items_set_data(hints, AGENT_HINT_RETRY_REASON, &reason, sizeof(reason)); 483 result = kAuthorizationResultAllow; 484 _cf_dictionary_iterate(engine->mechanism_agents, ^bool(CFTypeRef key __attribute__((__unused__)), CFTypeRef value) { 485 agent_t tempagent = (agent_t)value; 486 agent_clear_interrupt(tempagent); 487 return true; 488 }); 489 } 490 } 491 492 if (result == kAuthorizationResultAllow) { 493 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(mech), kAuthorizationResultAllow, NULL); 494 } else { 495 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(mech), (uint32_t)result, NULL); 496 break; 497 } 498 } 499 500done: 501 if ((result == kAuthorizationResultUserCanceled) || (result == kAuthorizationResultAllow)) { 502 // only make non-sticky context values available externally 503 auth_items_set_flags(context, kAuthorizationEnvironmentPassword, kAuthorizationContextFlagVolatile); 504 // <rdar://problem/16275827> Takauthorizationenvironmentusername should always be extractable 505 auth_items_set_flags(context, kAuthorizationEnvironmentUsername, kAuthorizationContextFlagExtractable); 506 auth_items_copy_with_flags(engine->context, context, kAuthorizationContextFlagExtractable | kAuthorizationContextFlagVolatile); 507 } else if (result == kAuthorizationResultDeny) { 508 auth_items_clear(engine->sticky_context); 509 // save off sticky values in context 510 auth_items_copy_with_flags(engine->sticky_context, context, kAuthorizationContextFlagSticky); 511 } 512 513 CFReleaseSafe(ccaudit); 514 CFReleaseSafe(context); 515 CFReleaseSafe(hints); 516 517 switch(result) 518 { 519 case kAuthorizationResultDeny: 520 return errAuthorizationDenied; 521 case kAuthorizationResultUserCanceled: 522 return errAuthorizationCanceled; 523 case kAuthorizationResultAllow: 524 return errAuthorizationSuccess; 525 case kAuthorizationResultUndefined: 526 return errAuthorizationInternal; 527 default: 528 { 529 LOGV("engine[%i]: unexpected error result", connection_get_pid(engine->conn)); 530 return errAuthorizationInternal; 531 } 532 } 533} 534 535static OSStatus 536_evaluate_authentication(engine_t engine, rule_t rule) 537{ 538 OSStatus status = errAuthorizationDenied; 539 ccaudit_t ccaudit = ccaudit_create(engine->proc, engine->auth, AUE_ssauthint); 540 LOGV("engine[%i]: evaluate authentication", connection_get_pid(engine->conn)); 541 _set_rule_hints(engine->hints, rule); 542 _set_session_hints(engine, rule); 543 544 CFArrayRef mechanisms = rule_get_mechanisms(rule); 545 if (!(CFArrayGetCount(mechanisms) > 0)) { 546 mechanisms = rule_get_mechanisms(engine->authenticateRule); 547 } 548 require_action(CFArrayGetCount(mechanisms) > 0, done, LOGV("engine[%i]: error no mechanisms found", connection_get_pid(engine->conn))); 549 550 int64_t ruleTries = rule_get_tries(rule); 551 for (engine->tries = 0; engine->tries < ruleTries; engine->tries++) { 552 553 auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason)); 554 auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries); 555 556 status = _evaluate_mechanisms(engine, mechanisms); 557 558 LOGV("engine[%i]: evaluate mechanisms result %i", connection_get_pid(engine->conn), status); 559 560 // successfully ran mechanisms to obtain credential 561 if (status == errAuthorizationSuccess) { 562 // deny is the default 563 status = errAuthorizationDenied; 564 565 credential_t newCred = NULL; 566 if (auth_items_exist(engine->context, "uid")) { 567 newCred = credential_create(auth_items_get_uint(engine->context, "uid")); 568 } else { 569 LOGV("engine[%i]: mechanism failed to return a valid uid", connection_get_pid(engine->conn)); 570 } 571 572 if (newCred) { 573 if (credential_get_valid(newCred)) { 574 LOG("UID %u authenticated as user %s (UID %u) for right '%s'", auth_token_get_uid(engine->auth), credential_get_name(newCred), credential_get_uid(newCred), engine->currentRightName); 575 ccaudit_log_success(ccaudit, newCred, engine->currentRightName); 576 } else { 577 LOG("UID %u failed to authenticate as user '%s' for right '%s'", auth_token_get_uid(engine->auth), auth_items_get_string(engine->context, "username"), engine->currentRightName); 578 ccaudit_log_failure(ccaudit, auth_items_get_string(engine->context, "username"), engine->currentRightName); 579 } 580 581 status = _evaluate_user_credential_for_rule(engine, newCred, rule, true, false, &engine->reason); 582 583 if (status == errAuthorizationSuccess) { 584 _engine_set_credential(engine, newCred, rule_get_shared(rule)); 585 CFReleaseSafe(newCred); 586 587 if (auth_token_least_privileged(engine->auth)) { 588 credential_t rightCred = credential_create_with_right(engine->currentRightName); 589 _engine_set_credential(engine, rightCred, rule_get_shared(rule)); 590 CFReleaseSafe(rightCred); 591 } 592 593 session_t session = auth_token_get_session(engine->auth); 594 if (credential_get_uid(newCred) == session_get_uid(session)) { 595 LOGV("engine[%i]: authenticated as the session owner", connection_get_pid(engine->conn)); 596 session_set_attributes(auth_token_get_session(engine->auth), AU_SESSION_FLAG_HAS_AUTHENTICATED); 597 } 598 599 break; 600 } 601 602 CFReleaseSafe(newCred); 603 } 604 605 } else if (status == errAuthorizationCanceled || status == errAuthorizationInternal) { 606 break; 607 } else if (status == errAuthorizationDenied) { 608 engine->reason = invalidPassphrase; 609 } 610 } 611 612 if (engine->tries == ruleTries) { 613 engine->reason = tooManyTries; 614 auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason)); 615 auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries); 616 _evaluate_mechanisms(engine, mechanisms); 617 ccaudit_log(ccaudit, engine->currentRightName, NULL, 1113); 618 } 619 620done: 621 CFReleaseSafe(ccaudit); 622 623 return status; 624} 625 626static bool 627_check_entitlement_for_rule(engine_t engine, rule_t rule) 628{ 629 bool entitled = false; 630 CFTypeRef value = NULL; 631 632 if (rule_check_flags(rule, RuleFlagEntitledAndGroup)) { 633 if (auth_token_has_entitlement_for_right(engine->auth, engine->currentRightName)) { 634 if (credential_check_membership(auth_token_get_credential(engine->auth), rule_get_group(rule))) { 635 LOGV("engine[%i]: creator of authorization has entitlement for right %s and is member of group '%s'", connection_get_pid(engine->conn), engine->currentRightName, rule_get_group(rule)); 636 entitled = true; 637 goto done; 638 } 639 } 640 } 641 642 if (rule_check_flags(rule, RuleFlagVPNEntitledAndGroup)) { 643 // com.apple.networking.vpn.configuration is an array we only check for it's existence 644 value = auth_token_copy_entitlement_value(engine->auth, "com.apple.networking.vpn.configuration"); 645 if (value) { 646 if (credential_check_membership(auth_token_get_credential(engine->auth), rule_get_group(rule))) { 647 LOGV("engine[%i]: creator of authorization has VPN entitlement and is member of group '%s'", connection_get_pid(engine->conn), rule_get_group(rule)); 648 entitled = true; 649 goto done; 650 } 651 } 652 } 653 654done: 655 CFReleaseSafe(value); 656 return entitled; 657} 658 659static OSStatus 660_evaluate_class_user(engine_t engine, rule_t rule) 661{ 662 __block OSStatus status = errAuthorizationDenied; 663 664 if (_check_entitlement_for_rule(engine,rule)) { 665 return errAuthorizationSuccess; 666 } 667 668 if (rule_get_allow_root(rule) && auth_token_get_uid(engine->auth) == 0) { 669 LOGV("engine[%i]: creator of authorization has uid == 0 granting right %s", connection_get_pid(engine->conn), engine->currentRightName); 670 return errAuthorizationSuccess; 671 } 672 673 if (!rule_get_authenticate_user(rule)) { 674 status = _evaluate_user_credential_for_rule(engine, engine->sessionCredential, rule, true, true, NULL); 675 676 if (status == errAuthorizationSuccess) { 677 return errAuthorizationSuccess; 678 } 679 680 return errAuthorizationDenied; 681 } 682 683 // First -- check all the credentials we have either acquired or currently have 684 _cf_set_iterate(engine->credentials, ^bool(CFTypeRef value) { 685 credential_t cred = (credential_t)value; 686 // Passed-in user credentials are allowed for least-privileged mode 687 if (auth_token_least_privileged(engine->auth) && !credential_is_right(cred) && credential_get_valid(cred)) { 688 status = _evaluate_user_credential_for_rule(engine, cred, rule, false, false, NULL); 689 if (errAuthorizationSuccess == status) { 690 credential_t rightCred = credential_create_with_right(engine->currentRightName); 691 _engine_set_credential(engine,rightCred,rule_get_shared(rule)); 692 CFReleaseSafe(rightCred); 693 return false; // exit loop 694 } 695 } 696 697 status = _evaluate_credential_for_rule(engine, cred, rule, false, false, NULL); 698 if (status == errAuthorizationSuccess) { 699 return false; // exit loop 700 } 701 return true; 702 }); 703 704 if (status == errAuthorizationSuccess) { 705 return status; 706 } 707 708 // Second -- go through the credentials associated to the authorization token session/auth token 709 _cf_set_iterate(engine->effectiveCredentials, ^bool(CFTypeRef value) { 710 credential_t cred = (credential_t)value; 711 status = _evaluate_credential_for_rule(engine, cred, rule, false, false, NULL); 712 if (status == errAuthorizationSuccess) { 713 // Add the credential we used to the output set. 714 _engine_set_credential(engine, cred, false); 715 return false; // exit loop 716 } 717 return true; 718 }); 719 720 if (status == errAuthorizationSuccess) { 721 return status; 722 } 723 724 // Finally - we didn't find a credential. Obtain a new credential if our flags let us do so. 725 if (!(engine->flags & kAuthorizationFlagExtendRights)) { 726 LOGV("engine[%i]: authorization denied (kAuthorizationFlagExtendRights not set)", connection_get_pid(engine->conn)); 727 return errAuthorizationDenied; 728 } 729 730 // authorization that timeout immediately cannot be preauthorized 731 if (engine->flags & kAuthorizationFlagPreAuthorize && rule_get_timeout(rule) == 0) { 732 return errAuthorizationSuccess; 733 } 734 735 if (!(engine->flags & kAuthorizationFlagInteractionAllowed)) { 736 LOGV("engine[%i]: Interaction not allowed (kAuthorizationFlagInteractionAllowed not set)", connection_get_pid(engine->conn)); 737 return errAuthorizationInteractionNotAllowed; 738 } 739 740 if (!(session_get_attributes(auth_token_get_session(engine->auth)) & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS)) { 741 LOGV("engine[%i]: Interaction not allowed (session has no ui access)", connection_get_pid(engine->conn)); 742 return errAuthorizationInteractionNotAllowed; 743 } 744 745 if (server_in_dark_wake()) { 746 LOGV("engine[%i]: authorization denied (in DarkWake)", connection_get_pid(engine->conn)); 747 return errAuthorizationDenied; 748 } 749 750 return _evaluate_authentication(engine, rule); 751} 752 753static OSStatus 754_evaluate_class_rule(engine_t engine, rule_t rule, bool *save_pwd) 755{ 756 __block OSStatus status = errAuthorizationDenied; 757 int64_t kofn = rule_get_kofn(rule); 758 759 uint32_t total = (uint32_t)rule_get_delegates_count(rule); 760 __block uint32_t success_count = 0; 761 __block uint32_t count = 0; 762 LOGV("engine[%i]: ** rule %s has %zi delegates kofn = %lli", connection_get_pid(engine->conn), rule_get_name(rule), total, kofn); 763 rule_delegates_iterator(rule, ^bool(rule_t delegate) { 764 count++; 765 766 if (kofn != 0 && success_count == kofn) { 767 status = errAuthorizationSuccess; 768 return false; 769 } 770 771 LOGV("engine[%i]: * evaluate rule %s (%i)", connection_get_pid(engine->conn), rule_get_name(delegate), count); 772 status = _evaluate_rule(engine, delegate, save_pwd); 773 774 // if status is cancel/internal error abort 775 if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal)) 776 return false; 777 778 if (status != errAuthorizationSuccess) { 779 if (kofn != 0) { 780 // if remaining is less than required abort 781 if ((total - count) < (kofn - success_count)) { 782 LOGD("engine[%i]: rule evaluation remaining: %i, required: %lli", connection_get_pid(engine->conn), (total - count), (kofn - success_count)); 783 return false; 784 } 785 return true; 786 } 787 return false; 788 } else { 789 success_count++; 790 return true; 791 } 792 }); 793 794 return status; 795} 796 797static OSStatus 798_evaluate_class_mechanism(engine_t engine, rule_t rule) 799{ 800 OSStatus status = errAuthorizationDenied; 801 CFArrayRef mechanisms = NULL; 802 803 require_action(rule_get_mechanisms_count(rule) > 0, done, status = errAuthorizationSuccess; LOGV("engine[%i]: no mechanisms specified", connection_get_pid(engine->conn))); 804 805 mechanisms = rule_get_mechanisms(rule); 806 807 if (server_in_dark_wake()) { 808 CFIndex count = CFArrayGetCount(mechanisms); 809 for (CFIndex i = 0; i < count; i++) { 810 if (!mechanism_is_privileged((mechanism_t)CFArrayGetValueAtIndex(mechanisms, i))) { 811 LOGV("engine[%i]: authorization denied (in DarkWake)", connection_get_pid(engine->conn)); 812 goto done; 813 } 814 } 815 } 816 817 int64_t ruleTries = rule_get_tries(rule); 818 engine->tries = 0; 819 do { 820 auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason)); 821 auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries); 822 823 status = _evaluate_mechanisms(engine, mechanisms); 824 LOGV("engine[%i]: evaluate mechanisms result %i", connection_get_pid(engine->conn), status); 825 826 if (status == errAuthorizationSuccess) { 827 credential_t newCred = NULL; 828 if (auth_items_exist(engine->context, "uid")) { 829 newCred = credential_create(auth_items_get_uint(engine->context, "uid")); 830 } else { 831 LOGV("engine[%i]: mechanism did not return a uid", connection_get_pid(engine->conn)); 832 } 833 834 if (newCred) { 835 _engine_set_credential(engine, newCred, rule_get_shared(rule)); 836 837 if (auth_token_least_privileged(engine->auth)) { 838 credential_t rightCred = credential_create_with_right(engine->currentRightName); 839 _engine_set_credential(engine, rightCred, rule_get_shared(rule)); 840 CFReleaseSafe(rightCred); 841 } 842 843 if (strcmp(engine->currentRightName, "system.login.console") == 0 && !auth_items_exist(engine->context, AGENT_CONTEXT_AUTO_LOGIN)) { 844 session_set_attributes(auth_token_get_session(engine->auth), AU_SESSION_FLAG_HAS_AUTHENTICATED); 845 } 846 847 CFReleaseSafe(newCred); 848 } 849 } 850 851 engine->tries++; 852 853 } while ( (status == errAuthorizationDenied) // only if we have an expected faulure we continue 854 && ((ruleTries == 0) || ((ruleTries > 0) && engine->tries < ruleTries))); // ruleTries == 0 means we try forever 855 // ruleTires > 0 means we try upto ruleTries times 856done: 857 return status; 858} 859 860static OSStatus 861_evaluate_rule(engine_t engine, rule_t rule, bool *save_pwd) 862{ 863 if (rule_check_flags(rule, RuleFlagEntitled)) { 864 if (auth_token_has_entitlement_for_right(engine->auth, engine->currentRightName)) { 865 LOGV("engine[%i]: rule allow, creator of authorization has entitlement for right %s", connection_get_pid(engine->conn), engine->currentRightName); 866 return errAuthorizationSuccess; 867 } 868 } 869 870 if (rule_check_flags(rule, RuleFlagRequireAppleSigned)) { 871 if (!auth_token_apple_signed(engine->auth)) { 872 LOGV("engine[%i]: rule deny, creator of authorization is not signed by apple", connection_get_pid(engine->conn)); 873 return errAuthorizationDenied; 874 } 875 } 876 877 *save_pwd |= rule_get_extract_password(rule); 878 879 switch (rule_get_class(rule)) { 880 case RC_ALLOW: 881 LOGV("engine[%i]: rule set to allow", connection_get_pid(engine->conn)); 882 return errAuthorizationSuccess; 883 case RC_DENY: 884 LOGV("engine[%i]: rule set to deny", connection_get_pid(engine->conn)); 885 return errAuthorizationDenied; 886 case RC_USER: 887 return _evaluate_class_user(engine, rule); 888 case RC_RULE: 889 return _evaluate_class_rule(engine, rule, save_pwd); 890 case RC_MECHANISM: 891 return _evaluate_class_mechanism(engine, rule); 892 default: 893 LOGV("engine[%i]: invalid class for rule or rule not found", connection_get_pid(engine->conn)); 894 return errAuthorizationInternal; 895 } 896} 897 898static rule_t 899_find_rule(engine_t engine, authdb_connection_t dbconn, const char * string) 900{ 901 rule_t r = NULL; 902 size_t sLen = strlen(string); 903 904 char * buf = calloc(1u, sLen + 1); 905 strlcpy(buf, string, sLen + 1); 906 char * ptr = buf + sLen; 907 __block int64_t count = 0; 908 909 for (;;) { 910 911 // lookup rule 912 authdb_step(dbconn, "SELECT COUNT(name) AS cnt FROM rules WHERE name = ? AND type = 1", 913 ^(sqlite3_stmt *stmt) { 914 sqlite3_bind_text(stmt, 1, buf, -1, NULL); 915 }, ^bool(auth_items_t data) { 916 count = auth_items_get_int64(data, "cnt"); 917 return false; 918 }); 919 920 if (count > 0) { 921 r = rule_create_with_string(buf, dbconn); 922 goto done; 923 } 924 925 // if buf ends with a . and we didn't find a rule remove . 926 if (*ptr == '.') { 927 *ptr = '\0'; 928 } 929 // find any remaining . and truncate the string 930 ptr = strrchr(buf, '.'); 931 if (ptr) { 932 *(ptr+1) = '\0'; 933 } else { 934 break; 935 } 936 } 937 938done: 939 free_safe(buf); 940 941 // set default if we didn't find a rule 942 if (r == NULL) { 943 r = rule_create_with_string("", dbconn); 944 if (rule_get_id(r) == 0) { 945 CFReleaseNull(r); 946 LOGE("engine[%i]: default rule lookup error (missing), using builtin defaults", connection_get_pid(engine->conn)); 947 r = rule_create_default(); 948 } 949 } 950 return r; 951} 952 953static void _parse_enviroment(engine_t engine, auth_items_t enviroment) 954{ 955 require(enviroment != NULL, done); 956 957#if DEBUG 958 LOGV("engine[%i]: Dumping Enviroment", connection_get_pid(engine->conn)); 959 _show_cf(enviroment); 960#endif 961 962 // Check if a credential was passed into the environment and we were asked to extend the rights 963 if (engine->flags & kAuthorizationFlagExtendRights) { 964 const char * user = auth_items_get_string(enviroment, kAuthorizationEnvironmentUsername); 965 const char * pass = auth_items_get_string(enviroment, kAuthorizationEnvironmentPassword); 966 bool shared = auth_items_exist(enviroment, kAuthorizationEnvironmentShared); 967 require(user != NULL, done); 968 969 struct passwd *pw = getpwnam(user); 970 require_action(pw != NULL, done, LOGE("engine[%i]: user not found %s", connection_get_pid(engine->conn), user)); 971 972 int checkpw_status = checkpw_internal(pw, pass ? pass : ""); 973 require_action(checkpw_status == CHECKPW_SUCCESS, done, LOGE("engine[%i]: checkpw() returned %d; failed to authenticate user %s (uid %u).", connection_get_pid(engine->conn), checkpw_status, pw->pw_name, pw->pw_uid)); 974 975 credential_t cred = credential_create(pw->pw_uid); 976 if (credential_get_valid(cred)) { 977 LOG("engine[%i]: checkpw() succeeded, creating credential for user %s", connection_get_pid(engine->conn), user); 978 _engine_set_credential(engine, cred, shared); 979 980 auth_items_set_string(engine->context, kAuthorizationEnvironmentUsername, user); 981 auth_items_set_string(engine->context, kAuthorizationEnvironmentPassword, pass ? pass : ""); 982 } 983 CFReleaseSafe(cred); 984 } 985 986done: 987 endpwent(); 988 return; 989} 990 991static bool _verify_sandbox(engine_t engine, const char * right) 992{ 993 pid_t pid = process_get_pid(engine->proc); 994 if (sandbox_check(pid, "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME, right)) { 995 LOGE("Sandbox denied authorizing right '%s' by client '%s' [%d]", right, process_get_code_url(engine->proc), pid); 996 return false; 997 } 998 999 pid = auth_token_get_pid(engine->auth); 1000 if (auth_token_get_sandboxed(engine->auth) && sandbox_check(pid, "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME, right)) { 1001 LOGE("Sandbox denied authorizing right '%s' for authorization created by '%s' [%d]", right, auth_token_get_code_url(engine->auth), pid); 1002 return false; 1003 } 1004 1005 return true; 1006} 1007 1008#pragma mark - 1009#pragma mark engine methods 1010 1011OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t enviroment, AuthorizationFlags flags) 1012{ 1013 __block OSStatus status = errAuthorizationSuccess; 1014 __block bool savePassword = false; 1015 ccaudit_t ccaudit = NULL; 1016 1017 require(rights != NULL, done); 1018 1019 ccaudit = ccaudit_create(engine->proc, engine->auth, AUE_ssauthorize); 1020 if (auth_rights_get_count(rights) > 0) { 1021 ccaudit_log(ccaudit, "begin evaluation", NULL, 0); 1022 } 1023 1024 engine->flags = flags; 1025 1026 if (enviroment) { 1027 _parse_enviroment(engine, enviroment); 1028 auth_items_copy(engine->hints, enviroment); 1029 } 1030 1031 auth_items_copy(engine->context, auth_token_get_context(engine->auth)); 1032 1033 engine->dismissed = false; 1034 auth_rights_clear(engine->grantedRights); 1035 1036 auth_rights_iterate(rights, ^bool(const char *key) { 1037 if (!key) 1038 return true; 1039 1040 1041 if (!_verify_sandbox(engine, key)) { 1042 status = errAuthorizationDenied; 1043 return false; 1044 } 1045 1046 authdb_connection_t dbconn = authdb_connection_acquire(server_get_database()); // get db handle 1047 1048 LOGV("engine[%i]: evaluate right %s", connection_get_pid(engine->conn), key); 1049 rule_t rule = _find_rule(engine, dbconn, key); 1050 const char * rule_name = rule_get_name(rule); 1051 if (rule_name && (strcasecmp(rule_name, "") == 0)) { 1052 rule_name = "default (not defined)"; 1053 } 1054 LOGV("engine[%i]: using rule %s", connection_get_pid(engine->conn), rule_name); 1055 1056 // only need the hints & mechanisms if we are going to show ui 1057 if (engine->flags & kAuthorizationFlagInteractionAllowed) { 1058 _set_right_hints(engine->hints, key); 1059 _set_localization_hints(dbconn, engine->hints, rule); 1060 if (!engine->authenticateRule) { 1061 engine->authenticateRule = rule_create_with_string("authenticate", dbconn); 1062 } 1063 } 1064 1065 authdb_connection_release(&dbconn); // release db handle 1066 1067 engine->currentRightName = key; 1068 engine->currentRule = rule; 1069 1070 ccaudit_log(ccaudit, key, rule_name, 0); 1071 1072 status = _evaluate_rule(engine, engine->currentRule, &savePassword); 1073 switch (status) { 1074 case errAuthorizationSuccess: 1075 auth_rights_add(engine->grantedRights, key); 1076 auth_rights_set_flags(engine->grantedRights, key, auth_rights_get_flags(rights,key)); 1077 1078 if ((engine->flags & kAuthorizationFlagPreAuthorize) && 1079 (rule_get_class(engine->currentRule) == RC_USER) && 1080 (rule_get_timeout(engine->currentRule) == 0)) { 1081 // FIXME: kAuthorizationFlagPreAuthorize => kAuthorizationFlagCanNotPreAuthorize ??? 1082 auth_rights_set_flags(engine->grantedRights, engine->currentRightName, kAuthorizationFlagPreAuthorize); 1083 } 1084 1085 LOG("Succeeded authorizing right '%s' by client '%s' [%d] for authorization created by '%s' [%d] (%X,%d)", 1086 key, process_get_code_url(engine->proc), process_get_pid(engine->proc), 1087 auth_token_get_code_url(engine->auth), auth_token_get_pid(engine->auth), engine->flags, auth_token_least_privileged(engine->auth)); 1088 break; 1089 case errAuthorizationDenied: 1090 case errAuthorizationInteractionNotAllowed: 1091 case errAuthorizationCanceled: 1092 if (engine->flags & kAuthorizationFlagInteractionAllowed) { 1093 LOG("Failed to authorize right '%s' by client '%s' [%d] for authorization created by '%s' [%d] (%X,%d) (%i)", 1094 key, process_get_code_url(engine->proc), process_get_pid(engine->proc), 1095 auth_token_get_code_url(engine->auth), auth_token_get_pid(engine->auth), engine->flags, auth_token_least_privileged(engine->auth), status); 1096 } else { 1097 LOGV("Failed to authorize right '%s' by client '%s' [%d] for authorization created by '%s' [%d] (%X,%d) (%i)", 1098 key, process_get_code_url(engine->proc), process_get_pid(engine->proc), 1099 auth_token_get_code_url(engine->auth), auth_token_get_pid(engine->auth), engine->flags, auth_token_least_privileged(engine->auth), status); 1100 } 1101 break; 1102 default: 1103 LOGE("engine[%i]: evaluate returned %i returning errAuthorizationInternal", connection_get_pid(engine->conn), status); 1104 status = errAuthorizationInternal; 1105 break; 1106 } 1107 1108 ccaudit_log_authorization(ccaudit, engine->currentRightName, status); 1109 1110 CFReleaseSafe(rule); 1111 engine->currentRightName = NULL; 1112 engine->currentRule = NULL; 1113 1114 auth_items_remove_with_flags(engine->hints, kEngineHintsFlagTemporary); 1115 1116 if (!(engine->flags & kAuthorizationFlagPartialRights) && (status != errAuthorizationSuccess)) { 1117 return false; 1118 } 1119 1120 return true; 1121 }); 1122 1123 if ((engine->flags & kAuthorizationFlagPartialRights) && (auth_rights_get_count(engine->grantedRights) > 0)) { 1124 status = errAuthorizationSuccess; 1125 } 1126 1127 if (engine->dismissed) { 1128 status = errAuthorizationDenied; 1129 } 1130 1131 LOGV("engine[%i]: authorize result: %i", connection_get_pid(engine->conn), status); 1132 1133 if ((engine->flags & kAuthorizationFlagExtendRights) && !(engine->flags & kAuthorizationFlagDestroyRights)) { 1134 _cf_set_iterate(engine->credentials, ^bool(CFTypeRef value) { 1135 credential_t cred = (credential_t)value; 1136 // skip all uid credentials when running in least privileged 1137 if (auth_token_least_privileged(engine->auth) && !credential_is_right(cred)) 1138 return true; 1139 1140 session_t session = auth_token_get_session(engine->auth); 1141 auth_token_set_credential(engine->auth, cred); 1142 if (credential_get_shared(cred)) { 1143 session_set_credential(session, cred); 1144 } 1145 if (credential_is_right(cred)) { 1146 LOGV("engine[%i]: adding least privileged %scredential %s to authorization", connection_get_pid(engine->conn), credential_get_shared(cred) ? "shared " : "", credential_get_name(cred)); 1147 } else { 1148 LOGV("engine[%i]: adding %scredential %s (%i) to authorization", connection_get_pid(engine->conn), credential_get_shared(cred) ? "shared " : "", credential_get_name(cred), credential_get_uid(cred)); 1149 } 1150 return true; 1151 }); 1152 } 1153 1154 if (status == errAuthorizationSuccess && savePassword) { 1155 auth_items_set_flags(engine->context, kAuthorizationEnvironmentPassword, kAuthorizationContextFlagExtractable); 1156 } 1157 1158 if ((status == errAuthorizationSuccess) || (status == errAuthorizationCanceled)) { 1159 auth_items_copy_with_flags(auth_token_get_context(engine->auth), engine->context, kAuthorizationContextFlagExtractable); 1160 } 1161 1162 if (auth_rights_get_count(rights) > 0) { 1163 ccaudit_log(ccaudit, "end evaluation", NULL, status); 1164 } 1165 1166#if DEBUG 1167 LOGV("engine[%i]: ********** Dumping auth->credentials **********", connection_get_pid(engine->conn)); 1168 auth_token_credentials_iterate(engine->auth, ^bool(credential_t cred) { 1169 _show_cf(cred); 1170 return true; 1171 }); 1172 LOGV("engine[%i]: ********** Dumping session->credentials **********", connection_get_pid(engine->conn)); 1173 session_credentials_iterate(auth_token_get_session(engine->auth), ^bool(credential_t cred) { 1174 _show_cf(cred); 1175 return true; 1176 }); 1177 LOGV("engine[%i]: ********** Dumping engine->context **********", connection_get_pid(engine->conn)); 1178 _show_cf(engine->context); 1179 LOGV("engine[%i]: ********** Dumping auth->context **********", connection_get_pid(engine->conn)); 1180 _show_cf(auth_token_get_context(engine->auth)); 1181 LOGV("engine[%i]: ********** Dumping granted rights **********", connection_get_pid(engine->conn)); 1182 _show_cf(engine->grantedRights); 1183#endif 1184 1185done: 1186 auth_items_clear(engine->context); 1187 auth_items_clear(engine->sticky_context); 1188 CFReleaseSafe(ccaudit); 1189 CFDictionaryRemoveAllValues(engine->mechanism_agents); 1190 1191 return status; 1192} 1193 1194static bool 1195_wildcard_right_exists(engine_t engine, const char * right) 1196{ 1197 // checks if a wild card right exists 1198 // ex: com.apple. system. 1199 bool exists = false; 1200 rule_t rule = NULL; 1201 authdb_connection_t dbconn = authdb_connection_acquire(server_get_database()); // get db handle 1202 require(dbconn != NULL, done); 1203 1204 rule = _find_rule(engine, dbconn, right); 1205 require(rule != NULL, done); 1206 1207 const char * ruleName = rule_get_name(rule); 1208 require(ruleName != NULL, done); 1209 size_t len = strlen(ruleName); 1210 require(len != 0, done); 1211 1212 if (ruleName[len-1] == '.') { 1213 exists = true; 1214 goto done; 1215 } 1216 1217done: 1218 authdb_connection_release(&dbconn); 1219 CFReleaseSafe(rule); 1220 1221 return exists; 1222} 1223 1224// Validate db right modification 1225 1226// meta rights are constructed as follows: 1227// we don't allow setting of wildcard rights, so you can only be more specific 1228// note that you should never restrict things with a wildcard right without disallowing 1229// changes to the entire domain. ie. 1230// system.privilege. -> never 1231// config.add.system.privilege. -> never 1232// config.modify.system.privilege. -> never 1233// config.delete.system.privilege. -> never 1234// For now we don't allow any configuration of configuration rules 1235// config.config. -> never 1236 1237OSStatus engine_verify_modification(engine_t engine, rule_t rule, bool remove, bool force_modify) 1238{ 1239 OSStatus status = errAuthorizationDenied; 1240 auth_rights_t checkRight = NULL; 1241 char buf[BUFSIZ]; 1242 memset(buf, 0, sizeof(buf)); 1243 1244 const char * right = rule_get_name(rule); 1245 require(right != NULL, done); 1246 size_t len = strlen(right); 1247 require(len != 0, done); 1248 1249 require_action(right[len-1] != '.', done, LOGE("engine[%i]: not allowed to set wild card rules", connection_get_pid(engine->conn))); 1250 1251 if (strncasecmp(right, kConfigRight, strlen(kConfigRight)) == 0) { 1252 // special handling of meta right change: 1253 // config.add. config.modify. config.remove. config.{}. 1254 // check for config.<right> (which always starts with config.config.) 1255 strlcat(buf, kConfigRight, sizeof(buf)); 1256 } else { 1257 bool existing = (rule_get_id(rule) != 0) ? true : _wildcard_right_exists(engine, right); 1258 if (!remove) { 1259 if (existing || force_modify) { 1260 strlcat(buf, kAuthorizationConfigRightModify,sizeof(buf)); 1261 } else { 1262 strlcat(buf, kAuthorizationConfigRightAdd, sizeof(buf)); 1263 } 1264 } else { 1265 if (existing) { 1266 strlcat(buf, kAuthorizationConfigRightRemove, sizeof(buf)); 1267 } else { 1268 status = errAuthorizationSuccess; 1269 goto done; 1270 } 1271 } 1272 } 1273 1274 strlcat(buf, right, sizeof(buf)); 1275 1276 checkRight = auth_rights_create(); 1277 auth_rights_add(checkRight, buf); 1278 status = engine_authorize(engine, checkRight, NULL, kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights); 1279 1280done: 1281 LOGV("engine[%i]: authorizing %s for db modification: %i", connection_get_pid(engine->conn), right, status); 1282 CFReleaseSafe(checkRight); 1283 return status; 1284} 1285 1286void 1287_engine_set_credential(engine_t engine, credential_t cred, bool shared) 1288{ 1289 LOGV("engine[%i]: adding %scredential %s (%i) to engine shared: %i", connection_get_pid(engine->conn), credential_get_shared(cred) ? "shared " : "", credential_get_name(cred), credential_get_uid(cred), shared); 1290 CFSetSetValue(engine->credentials, cred); 1291 if (shared) { 1292 credential_t sharedCred = credential_create_with_credential(cred, true); 1293 CFSetSetValue(engine->credentials, sharedCred); 1294 CFReleaseSafe(sharedCred); 1295 } 1296} 1297 1298auth_rights_t 1299engine_get_granted_rights(engine_t engine) 1300{ 1301 return engine->grantedRights; 1302} 1303 1304CFAbsoluteTime engine_get_time(engine_t engine) 1305{ 1306 return engine->now; 1307} 1308 1309void engine_destroy_agents(engine_t engine) 1310{ 1311 engine->dismissed = true; 1312 1313 _cf_dictionary_iterate(engine->mechanism_agents, ^bool(CFTypeRef key __attribute__((__unused__)), CFTypeRef value) { 1314 LOGD("engine[%i]: Destroying %s", connection_get_pid(engine->conn), mechanism_get_string((mechanism_t)key)); 1315 agent_t agent = (agent_t)value; 1316 agent_destroy(agent); 1317 1318 return true; 1319 }); 1320} 1321 1322void engine_interrupt_agent(engine_t engine) 1323{ 1324 _cf_dictionary_iterate(engine->mechanism_agents, ^bool(CFTypeRef key __attribute__((__unused__)), CFTypeRef value) { 1325 agent_t agent = (agent_t)value; 1326 agent_notify_interrupt(agent); 1327 return true; 1328 }); 1329} 1330