]> git.saurik.com Git - apple/security.git/blob - OSX/authd/engine.c
Security-58286.200.222.tar.gz
[apple/security.git] / OSX / authd / engine.c
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>
20 int checkpw_internal( const struct passwd *pw, const char* password );
21
22 #include <Security/AuthorizationTags.h>
23 #include <Security/AuthorizationTagsPriv.h>
24 #include <Security/AuthorizationPriv.h>
25 #include <Security/AuthorizationPlugin.h>
26 #include <LocalAuthentication/LAPublicDefines.h>
27 #include <LocalAuthentication/LAPrivateDefines.h>
28 #include <sandbox.h>
29 #include <coreauthd_spi.h>
30 #include <ctkloginhelper.h>
31
32
33 AUTHD_DEFINE_LOG
34
35 static void _set_process_hints(auth_items_t, process_t);
36 static void _set_process_immutable_hints(auth_items_t, process_t);
37 static void _set_auth_token_hints(auth_items_t, auth_items_t, auth_token_t);
38 static OSStatus _evaluate_user_credential_for_rule(engine_t, credential_t, rule_t, bool, bool, enum Reason *);
39 static void _engine_set_credential(engine_t, credential_t, bool);
40 static OSStatus _evaluate_rule(engine_t, rule_t, bool *);
41 static bool _preevaluate_class_rule(engine_t engine, rule_t rule);
42 static bool _preevaluate_rule(engine_t engine, rule_t rule);
43
44 static uint64_t global_engine_count;
45
46 enum {
47 kEngineHintsFlagTemporary = (1 << 30)
48 };
49
50 #pragma mark -
51 #pragma mark engine creation
52
53 struct _engine_s {
54 __AUTH_BASE_STRUCT_HEADER__;
55
56 connection_t conn;
57 process_t proc;
58 auth_token_t auth;
59
60 AuthorizationFlags flags;
61 auth_items_t hints;
62 auth_items_t context;
63 auth_items_t sticky_context;
64 auth_items_t immutable_hints;
65
66 auth_rights_t grantedRights;
67
68 CFTypeRef la_context;
69 bool preauthorizing;
70
71 enum Reason reason;
72 int32_t tries;
73
74 CFAbsoluteTime now;
75
76 credential_t sessionCredential;
77 CFMutableSetRef credentials;
78 CFMutableSetRef effectiveCredentials;
79
80 CFMutableDictionaryRef mechanism_agents;
81
82 // set only in engine_authorize
83 const char * currentRightName; // weak ref
84 rule_t currentRule; // weak ref
85
86 rule_t authenticateRule;
87
88 bool dismissed;
89
90 uint64_t engine_index;
91 };
92
93 static void
94 _engine_finalizer(CFTypeRef value)
95 {
96 engine_t engine = (engine_t)value;
97
98 CFReleaseNull(engine->mechanism_agents);
99 CFReleaseNull(engine->conn);
100 CFReleaseNull(engine->auth);
101 CFReleaseNull(engine->hints);
102 CFReleaseNull(engine->context);
103 CFReleaseNull(engine->immutable_hints);
104 CFReleaseNull(engine->sticky_context);
105 CFReleaseNull(engine->grantedRights);
106 CFReleaseNull(engine->sessionCredential);
107 CFReleaseNull(engine->credentials);
108 CFReleaseNull(engine->effectiveCredentials);
109 CFReleaseNull(engine->authenticateRule);
110 CFReleaseNull(engine->la_context);
111 }
112
113 AUTH_TYPE_INSTANCE(engine,
114 .init = NULL,
115 .copy = NULL,
116 .finalize = _engine_finalizer,
117 .equal = NULL,
118 .hash = NULL,
119 .copyFormattingDesc = NULL,
120 .copyDebugDesc = NULL
121 );
122
123 static CFTypeID engine_get_type_id() {
124 static CFTypeID type_id = _kCFRuntimeNotATypeID;
125 static dispatch_once_t onceToken;
126
127 dispatch_once(&onceToken, ^{
128 type_id = _CFRuntimeRegisterClass(&_auth_type_engine);
129 });
130
131 return type_id;
132 }
133
134 engine_t
135 engine_create(connection_t conn, auth_token_t auth)
136 {
137 engine_t engine = NULL;
138 require(conn != NULL, done);
139 require(auth != NULL, done);
140
141 engine = (engine_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, engine_get_type_id(), AUTH_CLASS_SIZE(engine), NULL);
142 require(engine != NULL, done);
143
144 engine->conn = (connection_t)CFRetain(conn);
145 engine->proc = connection_get_process(conn);
146 engine->auth = (auth_token_t)CFRetain(auth);
147
148 engine->hints = auth_items_create();
149 engine->context = auth_items_create();
150 engine->immutable_hints = auth_items_create();
151 engine->sticky_context = auth_items_create();
152 _set_process_hints(engine->hints, engine->proc);
153 _set_process_immutable_hints(engine->immutable_hints, engine->proc);
154 _set_auth_token_hints(engine->hints, engine->immutable_hints, auth);
155
156 engine->grantedRights = auth_rights_create();
157
158 engine->reason = noReason;
159
160 engine->preauthorizing = false;
161
162 engine->la_context = NULL;
163
164 engine->now = CFAbsoluteTimeGetCurrent();
165
166 session_update(auth_token_get_session(engine->auth));
167 engine->sessionCredential = credential_create(session_get_uid(auth_token_get_session(engine->auth)));
168 engine->credentials = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
169 engine->effectiveCredentials = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);
170
171 session_credentials_iterate(auth_token_get_session(engine->auth), ^bool(credential_t cred) {
172 CFSetAddValue(engine->effectiveCredentials, cred);
173 return true;
174 });
175
176 auth_token_credentials_iterate(engine->auth, ^bool(credential_t cred) {
177 // we added all session credentials already now just add all previously acquired credentials
178 if (!credential_get_shared(cred)) {
179 CFSetAddValue(engine->credentials, cred);
180 }
181 return true;
182 });
183
184 engine->mechanism_agents = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
185
186 engine->engine_index = global_engine_count++;
187 done:
188 return engine;
189 }
190
191 #pragma mark -
192 #pragma mark agent hints
193
194 void
195 _set_process_hints(auth_items_t hints, process_t proc)
196 {
197 // process information
198 RequestorType type = bundle;
199 auth_items_set_data(hints, AGENT_HINT_CLIENT_TYPE, &type, sizeof(type));
200 auth_items_set_int(hints, AGENT_HINT_CLIENT_PID, process_get_pid(proc));
201 auth_items_set_uint(hints, AGENT_HINT_CLIENT_UID, process_get_uid(proc));
202 }
203
204 void
205 _set_process_immutable_hints(auth_items_t immutable_hints, process_t proc)
206 {
207 // process information - immutable
208 auth_items_set_bool(immutable_hints, AGENT_HINT_CLIENT_SIGNED, process_apple_signed(proc));
209 auth_items_set_bool(immutable_hints, AGENT_HINT_CLIENT_FROM_APPLE, process_firstparty_signed(proc));
210 }
211
212 void
213 _set_auth_token_hints(auth_items_t hints, auth_items_t immutable_hints, auth_token_t auth)
214 {
215 auth_items_set_string(hints, AGENT_HINT_CLIENT_PATH, auth_token_get_code_url(auth));
216 auth_items_set_int(hints, AGENT_HINT_CREATOR_PID, auth_token_get_pid(auth));
217 const audit_info_s * info = auth_token_get_audit_info(auth);
218 auth_items_set_data(hints, AGENT_HINT_CREATOR_AUDIT_TOKEN, &info->opaqueToken, sizeof(info->opaqueToken));
219
220 process_t proc = process_create(info, auth_token_get_session(auth));
221 if (proc) {
222 auth_items_set_bool(immutable_hints, AGENT_HINT_CREATOR_SIGNED, process_apple_signed(proc));
223 auth_items_set_bool(immutable_hints, AGENT_HINT_CREATOR_FROM_APPLE, process_firstparty_signed(proc));
224 }
225 CFReleaseSafe(proc);
226 }
227
228 static void
229 _set_right_hints(auth_items_t hints, const char * right)
230 {
231 auth_items_set_string(hints, AGENT_HINT_AUTHORIZE_RIGHT, right);
232 }
233
234 static void
235 _set_rule_hints(auth_items_t hints, rule_t rule)
236 {
237 auth_items_set_string(hints, AGENT_HINT_AUTHORIZE_RULE, rule_get_name(rule));
238 const char * group = rule_get_group(rule);
239 if (rule_get_class(rule) == RC_USER && group != NULL) {
240 auth_items_set_string(hints, AGENT_HINT_REQUIRE_USER_IN_GROUP, group);
241 } else {
242 auth_items_remove(hints, AGENT_HINT_REQUIRE_USER_IN_GROUP);
243 }
244 }
245
246 static void
247 _set_localization_hints(authdb_connection_t dbconn, auth_items_t hints, rule_t rule)
248 {
249 char * key = calloc(1u, 128);
250
251 authdb_step(dbconn, "SELECT lang,value FROM prompts WHERE r_id = ?", ^(sqlite3_stmt *stmt) {
252 sqlite3_bind_int64(stmt, 1, rule_get_id(rule));
253 }, ^bool(auth_items_t data) {
254 snprintf(key, 128, "%s%s", kAuthorizationRuleParameterDescription, auth_items_get_string(data, "lang"));
255 auth_items_set_string(hints, key, auth_items_get_string(data, "value"));
256 auth_items_set_flags(hints, key, kEngineHintsFlagTemporary);
257 return true;
258 });
259
260 authdb_step(dbconn, "SELECT lang,value FROM buttons WHERE r_id = ?", ^(sqlite3_stmt *stmt) {
261 sqlite3_bind_int64(stmt, 1, rule_get_id(rule));
262 }, ^bool(auth_items_t data) {
263 snprintf(key, 128, "%s%s", kAuthorizationRuleParameterButton, auth_items_get_string(data, "lang"));
264 auth_items_set_string(hints, key, auth_items_get_string(data, "value"));
265 auth_items_set_flags(hints, key, kEngineHintsFlagTemporary);
266 return true;
267 });
268
269 free_safe(key);
270 }
271
272 static void
273 _set_session_hints(engine_t engine, rule_t rule)
274 {
275 os_log_debug(AUTHD_LOG, "engine %lld: ** prepare agent hints for rule %{public}s", engine->engine_index, rule_get_name(rule));
276 if (_evaluate_user_credential_for_rule(engine, engine->sessionCredential, rule, true, true, NULL) == errAuthorizationSuccess) {
277 const char * tmp = credential_get_name(engine->sessionCredential);
278 if (tmp != NULL) {
279 auth_items_set_string(engine->hints, AGENT_HINT_SUGGESTED_USER, tmp);
280 }
281 tmp = credential_get_realname(engine->sessionCredential);
282 if (tmp != NULL) {
283 auth_items_set_string(engine->hints, AGENT_HINT_SUGGESTED_USER_LONG, tmp);
284 }
285 } else {
286 auth_items_remove(engine->hints, AGENT_HINT_SUGGESTED_USER);
287 auth_items_remove(engine->hints, AGENT_HINT_SUGGESTED_USER_LONG);
288 }
289 }
290
291 #pragma mark -
292 #pragma mark right processing
293
294 static OSStatus
295 _evaluate_credential_for_rule(engine_t engine, credential_t cred, rule_t rule, bool ignoreShared, bool sessionOwner, enum Reason * reason)
296 {
297 if (auth_token_least_privileged(engine->auth)) {
298 if (credential_is_right(cred) && credential_get_valid(cred) && _compare_string(engine->currentRightName, credential_get_name(cred))) {
299 if (!ignoreShared) {
300 if (!rule_get_shared(rule) && credential_get_shared(cred)) {
301 os_log_error(AUTHD_LOG, "Shared right %{public}s (does NOT satisfy rule) (engine %lld)", credential_get_name(cred), engine->engine_index);
302 if (reason) { *reason = unknownReason; }
303 return errAuthorizationDenied;
304 }
305 }
306
307 return errAuthorizationSuccess;
308 } else {
309 if (reason) { *reason = unknownReason; }
310 return errAuthorizationDenied;
311 }
312 } else {
313 return _evaluate_user_credential_for_rule(engine,cred,rule,ignoreShared,sessionOwner, reason);
314 }
315 }
316
317 static OSStatus
318 _evaluate_user_credential_for_rule(engine_t engine, credential_t cred, rule_t rule, bool ignoreShared, bool sessionOwner, enum Reason * reason)
319 {
320 const char * cred_label = sessionOwner ? "session owner" : "credential";
321 os_log(AUTHD_LOG, "Validating %{public}s%{public}s %{public}s (%i) for %{public}s (engine %lld)", credential_get_shared(cred) ? "shared " : "",
322 cred_label,
323 credential_get_name(cred),
324 credential_get_uid(cred),
325 rule_get_name(rule),
326 engine->engine_index);
327
328 if (rule_get_class(rule) != RC_USER) {
329 os_log(AUTHD_LOG, "Invalid rule class %i (engine %lld)", rule_get_class(rule), engine->engine_index);
330 return errAuthorizationDenied;
331 }
332
333 if (credential_get_valid(cred) != true) {
334 os_log(AUTHD_LOG, "%{public}s %i invalid (does NOT satisfy rule) (engine %lld)", cred_label, credential_get_uid(cred), engine->engine_index);
335 if (reason) { *reason = invalidPassphrase; }
336 return errAuthorizationDenied;
337 }
338
339 if (engine->now - credential_get_creation_time(cred) > rule_get_timeout(rule)) {
340 os_log(AUTHD_LOG, "%{public}s %i expired '%f > %lli' (does NOT satisfy rule) (engine %lld)", cred_label, credential_get_uid(cred),
341 (engine->now - credential_get_creation_time(cred)), rule_get_timeout(rule), engine->engine_index);
342 if (reason) { *reason = unknownReason; }
343 return errAuthorizationDenied;
344 }
345
346 if (!ignoreShared) {
347 if (!rule_get_shared(rule) && credential_get_shared(cred)) {
348 os_log(AUTHD_LOG, "Shared %{public}s %i (does NOT satisfy rule) (engine %lld)", cred_label, credential_get_uid(cred), engine->engine_index);
349 if (reason) { *reason = unknownReason; }
350 return errAuthorizationDenied;
351 }
352 }
353
354 if (credential_get_uid(cred) == 0) {
355 os_log(AUTHD_LOG, "%{public}s %i has uid 0 (does satisfy rule) (engine %lld)", cred_label, credential_get_uid(cred), engine->engine_index);
356 return errAuthorizationSuccess;
357 }
358
359 if (rule_get_session_owner(rule)) {
360 if (credential_get_uid(cred) == session_get_uid(auth_token_get_session(engine->auth))) {
361 os_log(AUTHD_LOG, "%{public}s %i is session owner (does satisfy rule) (engine %lld)", cred_label, credential_get_uid(cred), engine->engine_index);
362 return errAuthorizationSuccess;
363 }
364 }
365
366 if (rule_get_group(rule) != NULL) {
367 do
368 {
369 // This allows testing a group modifier without prompting the user
370 // When (authenticate-user = false) we are just testing the creator uid.
371 // If a group modifier is enabled (RuleFlagEntitledAndGroup | RuleFlagVPNEntitledAndGroup)
372 // we want to skip the creator uid group check.
373 // group modifiers are checked early during the evaluation in _check_entitlement_for_rule
374 if (!rule_get_authenticate_user(rule)) {
375 if (rule_check_flags(rule, RuleFlagEntitledAndGroup | RuleFlagVPNEntitledAndGroup)) {
376 break;
377 }
378 }
379
380 if (credential_check_membership(cred, rule_get_group(rule))) {
381 os_log(AUTHD_LOG, "%{public}s %i is member of group %{public}s (does satisfy rule) (engine %lld)", cred_label, credential_get_uid(cred), rule_get_group(rule), engine->engine_index);
382 return errAuthorizationSuccess;
383 } else {
384 if (reason) { *reason = userNotInGroup; }
385 }
386 } while (0);
387 } else if (rule_get_session_owner(rule)) { // rule asks only if user is the session owner
388 if (reason) { *reason = unacceptableUser; }
389 }
390
391 os_log(AUTHD_LOG, "%{public}s %i (does NOT satisfy rule), reason %d (engine %lld)", cred_label, credential_get_uid(cred), reason ? *reason : -1, engine->engine_index);
392 return errAuthorizationDenied;
393 }
394
395 static agent_t
396 _get_agent(engine_t engine, mechanism_t mech, bool create, bool firstMech)
397 {
398 agent_t agent = (agent_t)CFDictionaryGetValue(engine->mechanism_agents, mech);
399 if (create && !agent) {
400 agent = agent_create(engine, mech, engine->auth, engine->proc, firstMech);
401 if (agent) {
402 CFDictionaryAddValue(engine->mechanism_agents, mech, agent);
403 CFReleaseSafe(agent);
404 }
405 }
406 return agent;
407 }
408
409 static uint64_t
410 _evaluate_builtin_mechanism(engine_t engine, mechanism_t mech)
411 {
412 uint64_t result = kAuthorizationResultDeny;
413
414 switch (mechanism_get_type(mech)) {
415 case kMechanismTypeEntitled:
416 if (auth_token_has_entitlement_for_right(engine->auth, engine->currentRightName)) {
417 result = kAuthorizationResultAllow;
418 }
419 break;
420 default:
421 break;
422 }
423
424 return result;
425 }
426
427
428 static bool
429 _extract_password_from_la(engine_t engine)
430 {
431 bool retval = false;
432
433 if (!engine->la_context) {
434 return retval;
435 }
436
437 // try to retrieve secret
438 CFDataRef passdata = LACopyCredential(engine->la_context, kLACredentialTypeExtractablePasscode, NULL);
439 if (passdata) {
440 if (CFDataGetBytePtr(passdata)) {
441 os_log_debug(AUTHD_LOG, "engine %lld: LA credentials retrieved", engine->engine_index);
442 auth_items_set_data(engine->context, kAuthorizationEnvironmentPassword, CFDataGetBytePtr(passdata), CFDataGetLength(passdata));
443 } else {
444 const char *empty_pass = "\0"; // authd code is unable to process empty strings so passing empty string as terminator only
445 os_log_debug(AUTHD_LOG, "engine %lld: LA credentials empty", engine->engine_index);
446 auth_items_set_data(engine->context, kAuthorizationEnvironmentPassword, empty_pass, 1);
447 }
448 CFRelease(passdata);
449 }
450 return retval;
451 }
452
453 static OSStatus
454 _evaluate_mechanisms(engine_t engine, CFArrayRef mechanisms)
455 {
456 uint64_t result = kAuthorizationResultAllow;
457 ccaudit_t ccaudit = ccaudit_create(engine->proc, engine->auth, AUE_ssauthmech);
458 auth_items_t context = auth_items_create();
459 auth_items_t hints = auth_items_create();
460
461 auth_items_copy(context, engine->context);
462 auth_items_copy(hints, engine->hints);
463 auth_items_copy(context, engine->sticky_context);
464
465 CFDictionaryRef la_result = NULL;
466
467 CFIndex count = CFArrayGetCount(mechanisms);
468 bool sheet_evaluation = false;
469 if (engine->la_context) {
470 int tmp = kLAOptionNotInteractive;
471 CFNumberRef key = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tmp);
472 tmp = 1;
473 CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &tmp);
474 if (key && value) {
475 CFMutableDictionaryRef options = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
476 CFDictionarySetValue(options, key, value);
477 la_result = LACopyResultOfPolicyEvaluation(engine->la_context, kLAPolicyDeviceOwnerAuthentication, options, NULL);
478 os_log_debug(AUTHD_LOG, "engine %lld: Retrieve LA evaluate result: %d", engine->engine_index, la_result != NULL);
479 CFReleaseSafe(options);
480 }
481 CFReleaseSafe(key);
482 CFReleaseSafe(value);
483 }
484
485 for (CFIndex i = 0; i < count; i++) {
486 mechanism_t mech = (mechanism_t)CFArrayGetValueAtIndex(mechanisms, i);
487
488 if (mechanism_get_type(mech)) {
489 os_log_debug(AUTHD_LOG, "engine %lld: running builtin mechanism %{public}s (%li of %li)", engine->engine_index, mechanism_get_string(mech), i+1, count);
490 result = _evaluate_builtin_mechanism(engine, mech);
491 } else {
492 bool shoud_run_agent = true; // evaluate comes from sheet -> we may not want to run standard SecurityAgent or authhost
493 if (engine->la_context) {
494 // sheet variant in progress
495 if (strcmp(mechanism_get_string(mech), "builtin:authenticate") == 0) {
496 // set the UID the same way as SecurityAgent would
497 if (auth_items_exist(engine->context, "sheet-uid")) {
498 os_log_debug(AUTHD_LOG, "engine %lld: setting sheet UID %d to the context", engine->engine_index, auth_items_get_uint(engine->context, "sheet-uid"));
499 auth_items_set_uint(engine->context, "uid", auth_items_get_uint(engine->context, "sheet-uid"));
500 }
501
502 // find out if sheet just provided credentials or did real authentication
503 // if password is provided or PAM service name exists, it means authd has to evaluate credentials
504 // otherwise we need to check la_result
505 if (auth_items_exist(engine->context, AGENT_CONTEXT_AP_PAM_SERVICE_NAME) || auth_items_exist(engine->context, kAuthorizationEnvironmentPassword)) {
506 // do not try to get credentials as it has been already passed by sheet
507 os_log_debug(AUTHD_LOG, "engine %lld: ignoring builtin sheet authenticate", engine->engine_index);
508 } else {
509 // sheet itself did the authenticate the user
510 os_log_debug(AUTHD_LOG, "engine %lld: running builtin sheet authenticate", engine->engine_index);
511 sheet_evaluation = true;
512 if (!la_result || TKGetSmartcardSetting(kTKEnforceSmartcard) != 0) {
513 result = kAuthorizationResultDeny; // no la_result => evaluate did not pass for sheet method. Enforced smartcard => no way to use sheet based evaluation
514 }
515 }
516 shoud_run_agent = false; // SecurityAgent should not be run for builtin:authenticate
517 } else if (strcmp(mechanism_get_string(mech), "builtin:authenticate,privileged") == 0) {
518 if (sheet_evaluation) {
519 os_log_debug(AUTHD_LOG, "engine %lld: running builtin sheet privileged authenticate", engine->engine_index);
520 shoud_run_agent = false;
521 if (!la_result || TKGetSmartcardSetting(kTKEnforceSmartcard) != 0) { // should not get here under normal circumstances but we need to handle this case as well
522 result = kAuthorizationResultDeny; // no la_result => evaluate did not pass. Enforced smartcard => no way to use sheet based evaluation
523 }
524 } else {
525 // should_run_agent has to be set to true because we want authorizationhost to verify the credentials
526 os_log_debug(AUTHD_LOG, "engine %lld: running sheet privileged authenticate", engine->engine_index);
527 }
528 }
529 }
530
531 if (shoud_run_agent) {
532 agent_t agent = _get_agent(engine, mech, true, i == 0);
533 require_action(agent != NULL, done, result = kAuthorizationResultUndefined; os_log_error(AUTHD_LOG, "Error creating mechanism agent (engine %lld)", engine->engine_index));
534
535 // check if any agent has been interrupted (it necessary if interrupt will come during creation)
536 CFIndex j;
537 agent_t agent1;
538 for (j = 0; j < i; j++) {
539 agent1 = _get_agent(engine, (mechanism_t)CFArrayGetValueAtIndex(mechanisms, j), false, j == 0);
540 if(agent1 && agent_get_state(agent1) == interrupting) {
541 break;
542 }
543 }
544 if (j < i) {
545 os_log(AUTHD_LOG, "engine %lld: mechanisms interrupted", engine->engine_index);
546 char * buf = NULL;
547 asprintf(&buf, "evaluation interrupted by %s; restarting evaluation there", mechanism_get_string(agent_get_mechanism(agent1)));
548 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(agent_get_mechanism(agent1)), kAuthorizationResultAllow, buf);
549 free_safe(buf);
550 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(mech), kAuthorizationResultAllow, NULL);
551 const char * token_name = auth_items_get_string(hints, AGENT_HINT_TOKEN_NAME);
552 if (token_name && strlen(token_name) == 0) {
553 auth_items_remove(hints, AGENT_HINT_TOKEN_NAME);
554 }
555 auth_items_copy(context, agent_get_context(agent1));
556 auth_items_copy(hints, agent_get_hints(agent1));
557
558 i = j - 1;
559
560 continue;
561 }
562
563 os_log(AUTHD_LOG, "engine %lld: running mechanism %{public}s (%li of %li)", engine->engine_index, mechanism_get_string(agent_get_mechanism(agent)), i+1, count);
564
565 result = agent_run(agent, hints, context, engine->immutable_hints);
566
567 auth_items_copy(context, agent_get_context(agent));
568 auth_items_copy(hints, agent_get_hints(agent));
569
570 bool interrupted = false;
571 for (CFIndex i2 = 0; i2 != i; i2++) {
572 agent_t agent2 = _get_agent(engine, (mechanism_t)CFArrayGetValueAtIndex(mechanisms, i2), false, i == 0);
573 if (agent2 && agent_get_state(agent2) == interrupting) {
574 agent_deactivate(agent);
575 interrupted = true;
576 i = i2 - 1;
577 char * buf = NULL;
578 asprintf(&buf, "evaluation interrupted by %s; restarting evaluation there", mechanism_get_string(agent_get_mechanism(agent2)));
579 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(agent_get_mechanism(agent2)), kAuthorizationResultAllow, buf);
580 free_safe(buf);
581 auth_items_copy(context, agent_get_context(agent2));
582 auth_items_copy(hints, agent_get_hints(agent2));
583 break;
584 }
585 }
586
587 // Empty token name means that token doesn't exist (e.g. SC was removed).
588 // Remove empty token name from hints for UI drawing logic.
589 const char * token_name = auth_items_get_string(hints, AGENT_HINT_TOKEN_NAME);
590 if (token_name && strlen(token_name) == 0) {
591 auth_items_remove(hints, AGENT_HINT_TOKEN_NAME);
592 }
593
594 if (interrupted) {
595 os_log_info(AUTHD_LOG, "Mechanisms interrupted (engine %lld)", engine->engine_index);
596 enum Reason reason = worldChanged;
597 auth_items_set_data(hints, AGENT_HINT_RETRY_REASON, &reason, sizeof(reason));
598 result = kAuthorizationResultAllow;
599 _cf_dictionary_iterate(engine->mechanism_agents, ^bool(CFTypeRef key __attribute__((__unused__)), CFTypeRef value) {
600 agent_t tempagent = (agent_t)value;
601 agent_clear_interrupt(tempagent);
602 return true;
603 });
604 }
605 }
606 }
607
608 if (result == kAuthorizationResultAllow) {
609 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(mech), kAuthorizationResultAllow, NULL);
610 } else {
611 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(mech), (uint32_t)result, NULL);
612 break;
613 }
614 }
615
616 done:
617 if ((result == kAuthorizationResultUserCanceled) || (result == kAuthorizationResultAllow)) {
618 // only make non-sticky context values available externally
619 auth_items_set_flags(context, kAuthorizationEnvironmentPassword, kAuthorizationContextFlagVolatile);
620 // <rdar://problem/16275827> Takauthorizationenvironmentusername should always be extractable
621 auth_items_set_flags(context, kAuthorizationEnvironmentUsername, kAuthorizationContextFlagExtractable);
622 auth_items_copy_with_flags(engine->context, context, kAuthorizationContextFlagExtractable | kAuthorizationContextFlagVolatile);
623 } else if (result == kAuthorizationResultDeny) {
624 auth_items_clear(engine->sticky_context);
625 // save off sticky values in context
626 auth_items_copy_with_flags(engine->sticky_context, context, kAuthorizationContextFlagSticky);
627 }
628
629 CFReleaseSafe(ccaudit);
630 CFReleaseSafe(context);
631 CFReleaseSafe(hints);
632 CFReleaseSafe(la_result);
633
634 switch(result)
635 {
636 case kAuthorizationResultDeny:
637 return errAuthorizationDenied;
638 case kAuthorizationResultUserCanceled:
639 return errAuthorizationCanceled;
640 case kAuthorizationResultAllow:
641 return errAuthorizationSuccess;
642 case kAuthorizationResultUndefined:
643 return errAuthorizationInternal;
644 default:
645 {
646 os_log_error(AUTHD_LOG, "Evaluate - unexpected result %llu (engine %lld)", result, engine->engine_index);
647 return errAuthorizationInternal;
648 }
649 }
650 }
651
652 static OSStatus
653 _evaluate_authentication(engine_t engine, rule_t rule)
654 {
655 OSStatus status = errAuthorizationDenied;
656 ccaudit_t ccaudit = ccaudit_create(engine->proc, engine->auth, AUE_ssauthint);
657 os_log_debug(AUTHD_LOG, "engine %lld: evaluate authentication", engine->engine_index);
658 _set_rule_hints(engine->hints, rule);
659 _set_session_hints(engine, rule);
660
661 CFArrayRef mechanisms = rule_get_mechanisms(rule);
662 if (!(CFArrayGetCount(mechanisms) > 0)) {
663 mechanisms = rule_get_mechanisms(engine->authenticateRule);
664 }
665 require_action(CFArrayGetCount(mechanisms) > 0, done, os_log_debug(AUTHD_LOG, "engine %lld: error - no mechanisms found", engine->engine_index));
666
667 int64_t ruleTries = rule_get_tries(rule);
668
669 if (engine->la_context) {
670 ruleTries = 1;
671 os_log_debug(AUTHD_LOG, "engine %lld: sheet authentication in progress, one try is enough", engine->engine_index);
672 }
673
674 for (engine->tries = 0; engine->tries < ruleTries; engine->tries++) {
675
676 auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason));
677 auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries);
678 status = _evaluate_mechanisms(engine, mechanisms);
679
680 os_log_debug(AUTHD_LOG, "engine %lld: evaluate mechanisms result %d", engine->engine_index, (int)status);
681
682 // successfully ran mechanisms to obtain credential
683 if (status == errAuthorizationSuccess) {
684 // deny is the default
685 status = errAuthorizationDenied;
686
687 credential_t newCred = NULL;
688 if (auth_items_exist(engine->context, "uid")) {
689 newCred = credential_create(auth_items_get_uint(engine->context, "uid"));
690 } else {
691 os_log_info(AUTHD_LOG, "Mechanism failed to return a valid uid (engine %lld)", engine->engine_index);
692 if (engine->la_context) {
693 // sheet failed so remove sheet reference and next time, standard dialog will be displayed
694 CFReleaseNull(engine->la_context);
695 }
696 }
697
698 if (newCred) {
699 if (credential_get_valid(newCred)) {
700 os_log(AUTHD_LOG, "UID %u authenticated as user %{public}s (UID %u) for right '%{public}s'", auth_token_get_uid(engine->auth), credential_get_name(newCred), credential_get_uid(newCred), engine->currentRightName);
701 ccaudit_log_success(ccaudit, newCred, engine->currentRightName);
702 } else {
703 os_log(AUTHD_LOG, "UID %u failed to authenticate as user '%{public}s' for right '%{public}s'", auth_token_get_uid(engine->auth), auth_items_get_string(engine->context, "username"), engine->currentRightName);
704 ccaudit_log_failure(ccaudit, auth_items_get_string(engine->context, "username"), engine->currentRightName);
705 }
706
707 status = _evaluate_user_credential_for_rule(engine, newCred, rule, true, false, &engine->reason);
708
709 if (status == errAuthorizationSuccess) {
710 _engine_set_credential(engine, newCred, rule_get_shared(rule));
711 CFReleaseSafe(newCred);
712
713 if (auth_token_least_privileged(engine->auth)) {
714 credential_t rightCred = credential_create_with_right(engine->currentRightName);
715 _engine_set_credential(engine, rightCred, rule_get_shared(rule));
716 CFReleaseSafe(rightCred);
717 }
718
719 session_t session = auth_token_get_session(engine->auth);
720 if (credential_get_uid(newCred) == session_get_uid(session)) {
721 os_log_debug(AUTHD_LOG, "engine %lld: authenticated as the session owner", engine->engine_index);
722 session_set_attributes(auth_token_get_session(engine->auth), AU_SESSION_FLAG_HAS_AUTHENTICATED);
723 }
724
725 break;
726 } else {
727 os_log_error(AUTHD_LOG, "User credential for rule failed (%d) (engine %lld)", (int)status, engine->engine_index);
728 }
729
730 CFReleaseSafe(newCred);
731 }
732
733 } else if (status == errAuthorizationCanceled || status == errAuthorizationInternal) {
734 os_log_error(AUTHD_LOG, "Evaluate cancelled or failed %d (engine %lld)", (int)status, engine->engine_index);
735 break;
736 } else if (status == errAuthorizationDenied) {
737 os_log_error(AUTHD_LOG, "Evaluate denied (engine %lld)", engine->engine_index);
738 engine->reason = invalidPassphrase;
739 }
740 }
741
742 if (engine->tries == ruleTries) {
743 engine->reason = tooManyTries;
744 auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason));
745 auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries);
746 ccaudit_log(ccaudit, engine->currentRightName, NULL, 1113);
747 }
748
749 done:
750 CFReleaseSafe(ccaudit);
751
752 return status;
753 }
754
755 static bool
756 _check_entitlement_for_rule(engine_t engine, rule_t rule)
757 {
758 bool entitled = false;
759 CFTypeRef value = NULL;
760
761 if (rule_check_flags(rule, RuleFlagEntitledAndGroup)) {
762 if (auth_token_has_entitlement_for_right(engine->auth, engine->currentRightName)) {
763 if (credential_check_membership(auth_token_get_credential(engine->auth), rule_get_group(rule))) {
764 os_log_info(AUTHD_LOG, "Creator of authorization has entitlement for right %{public}s and is member of group '%{public}s' (engine %lld)", engine->currentRightName, rule_get_group(rule), engine->engine_index);
765 entitled = true;
766 goto done;
767 }
768 }
769 }
770
771 if (rule_check_flags(rule, RuleFlagVPNEntitledAndGroup)) {
772 // com.apple.networking.vpn.configuration is an array we only check for it's existence
773 value = auth_token_copy_entitlement_value(engine->auth, "com.apple.networking.vpn.configuration");
774 if (value) {
775 if (credential_check_membership(auth_token_get_credential(engine->auth), rule_get_group(rule))) {
776 os_log_info(AUTHD_LOG, "Creator of authorization has VPN entitlement and is member of group '%{public}s' (engine %lld)", rule_get_group(rule), engine->engine_index);
777 entitled = true;
778 goto done;
779 }
780 }
781 }
782
783 done:
784 CFReleaseSafe(value);
785 return entitled;
786 }
787
788 static OSStatus
789 _evaluate_class_user(engine_t engine, rule_t rule)
790 {
791 __block OSStatus status = errAuthorizationDenied;
792
793 if (_check_entitlement_for_rule(engine,rule)) {
794 return errAuthorizationSuccess;
795 }
796
797 if (rule_get_allow_root(rule) && auth_token_get_uid(engine->auth) == 0) {
798 os_log_info(AUTHD_LOG, "Creator of authorization has uid == 0, granting right %{public}s (engine %lld)", engine->currentRightName, engine->engine_index);
799 return errAuthorizationSuccess;
800 }
801
802 if (!rule_get_authenticate_user(rule)) {
803 status = _evaluate_user_credential_for_rule(engine, engine->sessionCredential, rule, true, true, NULL);
804
805 if (status == errAuthorizationSuccess) {
806 return errAuthorizationSuccess;
807 }
808
809 return errAuthorizationDenied;
810 }
811
812 // First -- check all the credentials we have either acquired or currently have
813 _cf_set_iterate(engine->credentials, ^bool(CFTypeRef value) {
814 credential_t cred = (credential_t)value;
815 // Passed-in user credentials are allowed for least-privileged mode
816 if (auth_token_least_privileged(engine->auth) && !credential_is_right(cred) && credential_get_valid(cred)) {
817 status = _evaluate_user_credential_for_rule(engine, cred, rule, false, false, NULL);
818 if (errAuthorizationSuccess == status) {
819 credential_t rightCred = credential_create_with_right(engine->currentRightName);
820 _engine_set_credential(engine,rightCred,rule_get_shared(rule));
821 CFReleaseSafe(rightCred);
822 return false; // exit loop
823 }
824 }
825
826 status = _evaluate_credential_for_rule(engine, cred, rule, false, false, NULL);
827 if (status == errAuthorizationSuccess) {
828 return false; // exit loop
829 }
830 return true;
831 });
832
833 if (status == errAuthorizationSuccess) {
834 return status;
835 }
836
837 // Second -- go through the credentials associated to the authorization token session/auth token
838 _cf_set_iterate(engine->effectiveCredentials, ^bool(CFTypeRef value) {
839 credential_t cred = (credential_t)value;
840 status = _evaluate_credential_for_rule(engine, cred, rule, false, false, NULL);
841 if (status == errAuthorizationSuccess) {
842 // Add the credential we used to the output set.
843 _engine_set_credential(engine, cred, false);
844 return false; // exit loop
845 }
846 return true;
847 });
848
849 if (status == errAuthorizationSuccess) {
850 return status;
851 }
852
853 // Finally - we didn't find a credential. Obtain a new credential if our flags let us do so.
854 if (!(engine->flags & kAuthorizationFlagExtendRights)) {
855 os_log_error(AUTHD_LOG, "Fatal: authorization denied (kAuthorizationFlagExtendRights not set) (engine %lld)", engine->engine_index);
856 return errAuthorizationDenied;
857 }
858
859 // authorization that timeout immediately cannot be preauthorized
860 if (engine->flags & kAuthorizationFlagPreAuthorize && rule_get_timeout(rule) == 0) {
861 return errAuthorizationSuccess;
862 }
863
864 if (!engine->preauthorizing) {
865 if (!(engine->flags & kAuthorizationFlagInteractionAllowed)) {
866 os_log_error(AUTHD_LOG, "Fatal: interaction not allowed (kAuthorizationFlagInteractionAllowed not set) (engine %lld)", engine->engine_index);
867 return errAuthorizationInteractionNotAllowed;
868 }
869
870 if (!(session_get_attributes(auth_token_get_session(engine->auth)) & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS)) {
871 os_log_error(AUTHD_LOG, "Fatal: interaction not allowed (session has no ui access) (engine %lld)", engine->engine_index);
872 return errAuthorizationInteractionNotAllowed;
873 }
874
875 if (server_in_dark_wake()) {
876 os_log_error(AUTHD_LOG, "Fatal: authorization denied (DW) (engine %lld)", engine->engine_index);
877 return errAuthorizationDenied;
878 }
879 }
880
881 return _evaluate_authentication(engine, rule);
882 }
883
884 static OSStatus
885 _evaluate_class_rule(engine_t engine, rule_t rule, bool *save_pwd)
886 {
887 __block OSStatus status = errAuthorizationDenied;
888 int64_t kofn = rule_get_kofn(rule);
889
890 uint32_t total = (uint32_t)rule_get_delegates_count(rule);
891 __block uint32_t success_count = 0;
892 __block uint32_t count = 0;
893 os_log_debug(AUTHD_LOG, "engine %lld: ** rule %{public}s has %u delegates kofn = %lli", engine->engine_index, rule_get_name(rule), total, kofn);
894 rule_delegates_iterator(rule, ^bool(rule_t delegate) {
895 count++;
896
897 if (kofn != 0 && success_count == kofn) {
898 status = errAuthorizationSuccess;
899 return false;
900 }
901
902 os_log_debug(AUTHD_LOG, "engine %lld: * evaluate rule %{public}s (%i)", engine->engine_index, rule_get_name(delegate), count);
903 status = _evaluate_rule(engine, delegate, save_pwd);
904
905 // if status is cancel/internal error abort
906 if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
907 return false;
908
909 if (status != errAuthorizationSuccess) {
910 if (kofn != 0) {
911 // if remaining is less than required abort
912 if ((total - count) < (kofn - success_count)) {
913 os_log_debug(AUTHD_LOG, "engine %lld: rule evaluation remaining: %i, required: %lli", engine->engine_index, (total - count), (kofn - success_count));
914 return false;
915 }
916 return true;
917 }
918 return false;
919 } else {
920 success_count++;
921 return true;
922 }
923 });
924
925 return status;
926 }
927
928 static bool
929 _preevaluate_class_rule(engine_t engine, rule_t rule)
930 {
931 os_log_debug(AUTHD_LOG, "engine %lld: _preevaluate_class_rule %{public}s", engine->engine_index, rule_get_name(rule));
932
933 __block bool password_only = false;
934 rule_delegates_iterator(rule, ^bool(rule_t delegate) {
935 if (_preevaluate_rule(engine, delegate)) {
936 password_only = true;
937 return false;
938 }
939 return true;
940 });
941
942 return password_only;
943 }
944
945 static OSStatus
946 _evaluate_class_mechanism(engine_t engine, rule_t rule)
947 {
948 OSStatus status = errAuthorizationDenied;
949 CFArrayRef mechanisms = NULL;
950
951 require_action(rule_get_mechanisms_count(rule) > 0, done, status = errAuthorizationSuccess; os_log_error(AUTHD_LOG, "Fatal: no mechanisms specified (engine %lld)", engine->engine_index));
952
953 mechanisms = rule_get_mechanisms(rule);
954
955 if (server_in_dark_wake()) {
956 CFIndex count = CFArrayGetCount(mechanisms);
957 for (CFIndex i = 0; i < count; i++) {
958 if (!mechanism_is_privileged((mechanism_t)CFArrayGetValueAtIndex(mechanisms, i))) {
959 os_log_error(AUTHD_LOG, "Fatal: authorization denied (in DW) (engine %lld)", engine->engine_index);
960 goto done;
961 }
962 }
963 }
964
965 int64_t ruleTries = rule_get_tries(rule);
966 engine->tries = 0;
967 do {
968 auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason));
969 auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries);
970
971 status = _evaluate_mechanisms(engine, mechanisms);
972 os_log_debug(AUTHD_LOG, "engine %lld: evaluate mechanisms result %d", engine->engine_index, (int)status);
973
974 if (status == errAuthorizationSuccess) {
975 credential_t newCred = NULL;
976 if (auth_items_exist(engine->context, "uid")) {
977 newCred = credential_create(auth_items_get_uint(engine->context, "uid"));
978 } else {
979 os_log_info(AUTHD_LOG, "Mechanism did not return a uid (engine %lld)", engine->engine_index);
980 }
981
982 if (newCred) {
983 _engine_set_credential(engine, newCred, rule_get_shared(rule));
984
985 if (auth_token_least_privileged(engine->auth)) {
986 credential_t rightCred = credential_create_with_right(engine->currentRightName);
987 _engine_set_credential(engine, rightCred, rule_get_shared(rule));
988 CFReleaseSafe(rightCred);
989 }
990
991 if (strcmp(engine->currentRightName, "system.login.console") == 0 && !auth_items_exist(engine->context, AGENT_CONTEXT_AUTO_LOGIN)) {
992 session_set_attributes(auth_token_get_session(engine->auth), AU_SESSION_FLAG_HAS_AUTHENTICATED);
993 }
994
995 CFReleaseSafe(newCred);
996 }
997 }
998
999 engine->tries++;
1000
1001 } while ( (status == errAuthorizationDenied) // only if we have an expected faulure we continue
1002 && ((ruleTries == 0) || ((ruleTries > 0) && engine->tries < ruleTries))); // ruleTries == 0 means we try forever
1003 // ruleTires > 0 means we try upto ruleTries times
1004 done:
1005 return status;
1006 }
1007
1008 // TODO: Remove when all clients have adopted entitlement
1009 static bool
1010 enforced_entitlement(void)
1011 {
1012 bool enforced_enabled = false;
1013 //sudo defaults write /Library/Preferences/com.apple.authd enforceEntitlement -bool true
1014 CFTypeRef enforce = (CFNumberRef)CFPreferencesCopyValue(CFSTR("enforceEntitlement"), CFSTR(SECURITY_AUTH_NAME), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
1015 if (enforce && CFGetTypeID(enforce) == CFBooleanGetTypeID()) {
1016 enforced_enabled = CFBooleanGetValue((CFBooleanRef)enforce);
1017 os_log_debug(AUTHD_LOG, "enforceEntitlement for extract password: %{public}s", enforced_enabled ? "enabled" : "disabled");
1018 }
1019 CFReleaseSafe(enforce);
1020
1021 return enforced_enabled;
1022 }
1023
1024 static OSStatus
1025 _evaluate_rule(engine_t engine, rule_t rule, bool *save_pwd)
1026 {
1027 if (rule_check_flags(rule, RuleFlagEntitled)) {
1028 if (auth_token_has_entitlement_for_right(engine->auth, engine->currentRightName)) {
1029 os_log_debug(AUTHD_LOG, "engine %lld: rule allow, creator of authorization has entitlement for right %{public}s", engine->engine_index, engine->currentRightName);
1030 return errAuthorizationSuccess;
1031 }
1032 }
1033
1034 // check apple signature also for every sheet authorization + disable this check for debug builds
1035 if (engine->la_context || rule_check_flags(rule, RuleFlagRequireAppleSigned)) {
1036 if (!auth_token_apple_signed(engine->auth)) {
1037 #ifdef NDEBUG
1038 os_log_error(AUTHD_LOG, "Rule deny, creator of authorization is not signed by Apple (engine %lld)", engine->engine_index);
1039 return errAuthorizationDenied;
1040 #else
1041 os_log_debug(AUTHD_LOG, "engine %lld: in release mode, this rule would be denied because creator of authorization is not signed by Apple", engine->engine_index);
1042 #endif
1043 }
1044 }
1045
1046 if (rule_get_extract_password(rule)) {
1047 // check if process is entitled to extract password
1048 CFTypeRef extract_password_entitlement = auth_token_copy_entitlement_value(engine->auth, "com.apple.authorization.extract-password");
1049 if (extract_password_entitlement && (CFGetTypeID(extract_password_entitlement) == CFBooleanGetTypeID()) && extract_password_entitlement == kCFBooleanTrue) {
1050 *save_pwd = TRUE;
1051 os_log_debug(AUTHD_LOG, "engine %lld: authorization allowed to extract password", engine->engine_index);
1052 } else {
1053 os_log_debug(AUTHD_LOG, "engine %lld: authorization NOT allowed to extract password", engine->engine_index);
1054 }
1055 CFReleaseSafe(extract_password_entitlement);
1056 }
1057
1058 // TODO: Remove when all clients have adopted entitlement
1059 if (!enforced_entitlement()) {
1060 *save_pwd |= rule_get_extract_password(rule);
1061 }
1062
1063 switch (rule_get_class(rule)) {
1064 case RC_ALLOW:
1065 os_log(AUTHD_LOG, "Rule set to allow (engine %lld)", engine->engine_index);
1066 return errAuthorizationSuccess;
1067 case RC_DENY:
1068 os_log(AUTHD_LOG, "Rule set to deny (engine %lld)", engine->engine_index);
1069 return errAuthorizationDenied;
1070 case RC_USER:
1071 return _evaluate_class_user(engine, rule);
1072 case RC_RULE:
1073 return _evaluate_class_rule(engine, rule, save_pwd);
1074 case RC_MECHANISM:
1075 return _evaluate_class_mechanism(engine, rule);
1076 default:
1077 os_log_error(AUTHD_LOG, "Invalid class for rule or rule not found: %{public}s (engine %lld)", rule_get_name(rule), engine->engine_index);
1078 return errAuthorizationInternal;
1079 }
1080 }
1081
1082 // returns true if this rule or its children contain RC_USER rule with password_only==true
1083 static bool
1084 _preevaluate_rule(engine_t engine, rule_t rule)
1085 {
1086 os_log_debug(AUTHD_LOG, "engine %lld: _preevaluate_rule %{public}s", engine->engine_index, rule_get_name(rule));
1087
1088 switch (rule_get_class(rule)) {
1089 case RC_ALLOW:
1090 case RC_DENY:
1091 return false;
1092 case RC_USER:
1093 return rule_get_password_only(rule);
1094 case RC_RULE:
1095 return _preevaluate_class_rule(engine, rule);
1096 case RC_MECHANISM:
1097 return false;
1098 default:
1099 return false;
1100 }
1101 }
1102
1103 static rule_t
1104 _find_rule(engine_t engine, authdb_connection_t dbconn, const char * string)
1105 {
1106 rule_t r = NULL;
1107 size_t sLen = strlen(string);
1108
1109 char * buf = calloc(1u, sLen + 1);
1110 strlcpy(buf, string, sLen + 1);
1111 char * ptr = buf + sLen;
1112 __block int64_t count = 0;
1113
1114 for (;;) {
1115
1116 // lookup rule
1117 authdb_step(dbconn, "SELECT COUNT(name) AS cnt FROM rules WHERE name = ? AND type = 1",
1118 ^(sqlite3_stmt *stmt) {
1119 sqlite3_bind_text(stmt, 1, buf, -1, NULL);
1120 }, ^bool(auth_items_t data) {
1121 count = auth_items_get_int64(data, "cnt");
1122 return false;
1123 });
1124
1125 if (count > 0) {
1126 r = rule_create_with_string(buf, dbconn);
1127 goto done;
1128 }
1129
1130 // if buf ends with a . and we didn't find a rule remove .
1131 if (*ptr == '.') {
1132 *ptr = '\0';
1133 }
1134 // find any remaining . and truncate the string
1135 ptr = strrchr(buf, '.');
1136 if (ptr) {
1137 *(ptr+1) = '\0';
1138 } else {
1139 break;
1140 }
1141 }
1142
1143 done:
1144 free_safe(buf);
1145
1146 // set default if we didn't find a rule
1147 if (r == NULL) {
1148 r = rule_create_with_string("", dbconn);
1149 if (rule_get_id(r) == 0) {
1150 CFReleaseNull(r);
1151 os_log_error(AUTHD_LOG, "Default rule lookup error (missing), using builtin defaults (engine %lld)", engine->engine_index);
1152 r = rule_create_default();
1153 }
1154 }
1155 return r;
1156 }
1157
1158 static void _parse_environment(engine_t engine, auth_items_t environment)
1159 {
1160 require(environment != NULL, done);
1161
1162 #if DEBUG
1163 os_log_debug(AUTHD_LOG, "engine %lld: Dumping Environment: %@", engine->engine_index, environment);
1164 #endif
1165
1166 // Check if a credential was passed into the environment and we were asked to extend the rights
1167 if (engine->flags & kAuthorizationFlagExtendRights && !(engine->flags & kAuthorizationFlagSheet)) {
1168 const char * user = auth_items_get_string(environment, kAuthorizationEnvironmentUsername);
1169 const char * pass = auth_items_get_string(environment, kAuthorizationEnvironmentPassword);
1170 const bool password_was_used = auth_items_get_string(environment, AGENT_CONTEXT_AP_PAM_SERVICE_NAME) == nil; // AGENT_CONTEXT_AP_PAM_SERVICE_NAME in the context means alternative PAM was used
1171 require(password_was_used == true, done);
1172
1173 bool shared = auth_items_exist(environment, kAuthorizationEnvironmentShared);
1174 require_action(user != NULL, done, os_log_debug(AUTHD_LOG, "engine %lld: user not used password", engine->engine_index));
1175
1176 struct passwd *pw = getpwnam(user);
1177 require_action(pw != NULL, done, os_log_error(AUTHD_LOG, "User not found %{public}s (engine %lld)", user, engine->engine_index));
1178
1179 int checkpw_status = checkpw_internal(pw, pass ? pass : "");
1180 require_action(checkpw_status == CHECKPW_SUCCESS, done, os_log_error(AUTHD_LOG, "engine %lld: checkpw() returned %d; failed to authenticate user %{public}s (uid %u).", engine->engine_index, checkpw_status, pw->pw_name, pw->pw_uid));
1181
1182 credential_t cred = credential_create(pw->pw_uid);
1183 if (credential_get_valid(cred)) {
1184 os_log_info(AUTHD_LOG, "checkpw() succeeded, creating credential for user %{public}s (engine %lld)", user, engine->engine_index);
1185 _engine_set_credential(engine, cred, shared);
1186
1187 auth_items_set_string(engine->context, kAuthorizationEnvironmentUsername, user);
1188 auth_items_set_string(engine->context, kAuthorizationEnvironmentPassword, pass ? pass : "");
1189 }
1190 CFReleaseSafe(cred);
1191 }
1192
1193 done:
1194 endpwent();
1195 return;
1196 }
1197
1198 static bool _verify_sandbox(engine_t engine, const char * right)
1199 {
1200 pid_t pid = process_get_pid(engine->proc);
1201 if (sandbox_check(pid, "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME, right)) {
1202 os_log_error(AUTHD_LOG, "Sandbox denied authorizing right '%{public}s' by client '%{public}s' [%d] (engine %lld)", right, process_get_code_url(engine->proc), pid, engine->engine_index);
1203 return false;
1204 }
1205
1206 pid = auth_token_get_pid(engine->auth);
1207 if (auth_token_get_sandboxed(engine->auth) && sandbox_check_by_audit_token(auth_token_get_audit_info(engine->auth)->opaqueToken, "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME, right)) {
1208 os_log_error(AUTHD_LOG, "Sandbox denied authorizing right '%{public}s' for authorization created by '%{public}s' [%d] (engine %lld)", right, auth_token_get_code_url(engine->auth), pid, engine->engine_index);
1209 return false;
1210 }
1211
1212 return true;
1213 }
1214
1215 #pragma mark -
1216 #pragma mark engine methods
1217
1218 OSStatus engine_preauthorize(engine_t engine, auth_items_t credentials)
1219 {
1220 os_log(AUTHD_LOG, "engine %lld: preauthorizing", engine->engine_index);
1221
1222 OSStatus status = errAuthorizationDenied;
1223 bool save_password = false;
1224 CFTypeRef extract_password_entitlement = auth_token_copy_entitlement_value(engine->auth, "com.apple.authorization.extract-password");
1225 if (extract_password_entitlement && (CFGetTypeID(extract_password_entitlement) == CFBooleanGetTypeID()) && extract_password_entitlement == kCFBooleanTrue) {
1226 save_password = true;
1227 os_log_debug(AUTHD_LOG, "engine %lld: authorization allowed to extract password", engine->engine_index);
1228 } else {
1229 os_log_debug(AUTHD_LOG, "engine %lld: authorization NOT allowed to extract password", engine->engine_index);
1230 }
1231 CFReleaseSafe(extract_password_entitlement);
1232
1233 // TODO: Remove when all clients have adopted entitlement
1234 if (!enforced_entitlement()) {
1235 save_password = true;
1236 }
1237
1238 engine->flags = kAuthorizationFlagExtendRights;
1239 engine->preauthorizing = true;
1240 CFAssignRetained(engine->la_context, engine_copy_context(engine, credentials));
1241 _extract_password_from_la(engine);
1242
1243 const char *user = auth_items_get_string(credentials, kAuthorizationEnvironmentUsername);
1244 require(user, done);
1245
1246 auth_items_set_string(engine->context, kAuthorizationEnvironmentUsername, user);
1247 struct passwd *pwd = getpwnam(user);
1248 require(pwd, done);
1249
1250 auth_items_set_int(engine->context, AGENT_CONTEXT_UID, pwd->pw_uid);
1251
1252 const char *service = auth_items_get_string(credentials, AGENT_CONTEXT_AP_PAM_SERVICE_NAME);
1253
1254 if (service) {
1255 auth_items_set_string(engine->context, AGENT_CONTEXT_AP_USER_NAME, user);
1256 auth_items_set_string(engine->context, AGENT_CONTEXT_AP_PAM_SERVICE_NAME, service);
1257 }
1258
1259 if (auth_items_exist(credentials, AGENT_CONTEXT_AP_TOKEN)) {
1260 size_t datalen = 0;
1261 const void *data = auth_items_get_data(credentials, AGENT_CONTEXT_AP_TOKEN, &datalen);
1262 if (data) {
1263 auth_items_set_data(engine->context, AGENT_CONTEXT_AP_TOKEN, data, datalen);
1264 }
1265 }
1266
1267 auth_items_t decrypted_items = auth_items_create();
1268 require_action(decrypted_items != NULL, done, os_log_error(AUTHD_LOG, "Unable to create items (engine %lld)", engine->engine_index));
1269 auth_items_content_copy(decrypted_items, auth_token_get_context(engine->auth));
1270 auth_items_decrypt(decrypted_items, auth_token_get_encryption_key(engine->auth));
1271 auth_items_copy(engine->context, decrypted_items);
1272 CFReleaseSafe(decrypted_items);
1273
1274 engine->dismissed = false;
1275 auth_rights_clear(engine->grantedRights);
1276
1277 rule_t rule = rule_create_preauthorization();
1278 engine->currentRightName = rule_get_name(rule);
1279 engine->currentRule = rule;
1280 status = _evaluate_rule(engine, rule, &save_password);
1281 switch (status) {
1282 case errAuthorizationSuccess:
1283 os_log(AUTHD_LOG, "Succeeded preauthorizing client '%{public}s' [%d] for authorization created by '%{public}s' [%d] (%X,%d) (engine %lld)",
1284 process_get_code_url(engine->proc), process_get_pid(engine->proc),
1285 auth_token_get_code_url(engine->auth), auth_token_get_pid(engine->auth), (unsigned int)engine->flags, auth_token_least_privileged(engine->auth), engine->engine_index);
1286 status = errAuthorizationSuccess;
1287 break;
1288 case errAuthorizationDenied:
1289 case errAuthorizationInteractionNotAllowed:
1290 case errAuthorizationCanceled:
1291 os_log(AUTHD_LOG, "Failed to preauthorize client '%{public}s' [%d] for authorization created by '%{public}s' [%d] (%X,%d) (%i) (engine %lld)",
1292 process_get_code_url(engine->proc), process_get_pid(engine->proc),
1293 auth_token_get_code_url(engine->auth), auth_token_get_pid(engine->auth), (unsigned int)engine->flags, auth_token_least_privileged(engine->auth), (int)status, engine->engine_index);
1294 break;
1295 default:
1296 os_log_error(AUTHD_LOG, "Preauthorize returned %d => returning errAuthorizationInternal (engine %lld)", (int)status, engine->engine_index);
1297 status = errAuthorizationInternal;
1298 break;
1299 }
1300
1301 CFReleaseSafe(rule);
1302
1303 if (engine->dismissed) {
1304 os_log_error(AUTHD_LOG, "Engine dismissed (engine %lld)", engine->engine_index);
1305 status = errAuthorizationDenied;
1306 }
1307
1308 os_log_debug(AUTHD_LOG, "engine %lld: preauthorize result: %d", engine->engine_index, (int)status);
1309
1310 _cf_set_iterate(engine->credentials, ^bool(CFTypeRef value) {
1311 credential_t cred = (credential_t)value;
1312 // skip all uid credentials when running in least privileged
1313 if (auth_token_least_privileged(engine->auth) && !credential_is_right(cred))
1314 return true;
1315
1316 session_t session = auth_token_get_session(engine->auth);
1317 auth_token_set_credential(engine->auth, cred);
1318 if (credential_get_shared(cred)) {
1319 session_set_credential(session, cred);
1320 }
1321 if (credential_is_right(cred)) {
1322 os_log(AUTHD_LOG, "engine %lld: adding least privileged %{public}scredential %{public}s to authorization", engine->engine_index, credential_get_shared(cred) ? "shared " : "", credential_get_name(cred));
1323 } else {
1324 os_log(AUTHD_LOG, "engine %lld: adding %{public}scredential %{public}s (%i) to authorization", engine->engine_index, credential_get_shared(cred) ? "shared " : "", credential_get_name(cred), credential_get_uid(cred));
1325 }
1326 return true;
1327 });
1328
1329
1330 if (status == errAuthorizationSuccess && save_password) {
1331 auth_items_set_flags(engine->context, kAuthorizationEnvironmentPassword, kAuthorizationContextFlagExtractable);
1332 }
1333
1334 if ((status == errAuthorizationSuccess) || (status == errAuthorizationCanceled)) {
1335 auth_items_t encrypted_items = auth_items_create();
1336 require_action(encrypted_items != NULL, done, os_log_error(AUTHD_LOG, "Unable to create items (engine %lld)", engine->engine_index));
1337 auth_items_content_copy_with_flags(encrypted_items, engine->context, kAuthorizationContextFlagExtractable);
1338 #if DEBUG
1339 os_log_debug(AUTHD_LOG, "engine %lld: ********** Dumping preauthorized context for encryption **********", engine->engine_index);
1340 os_log_debug(AUTHD_LOG, "%@", encrypted_items);
1341 #endif
1342 auth_items_encrypt(encrypted_items, auth_token_get_encryption_key(engine->auth));
1343 auth_items_copy_with_flags(auth_token_get_context(engine->auth), encrypted_items, kAuthorizationContextFlagExtractable);
1344 os_log_debug(AUTHD_LOG, "engine %lld: encrypted preauthorization context data", engine->engine_index);
1345 CFReleaseSafe(encrypted_items);
1346 }
1347
1348 done:
1349 engine->preauthorizing = false;
1350 auth_items_clear(engine->context);
1351 auth_items_clear(engine->sticky_context);
1352 CFDictionaryRemoveAllValues(engine->mechanism_agents);
1353 return status;
1354 }
1355
1356 OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t environment, AuthorizationFlags flags)
1357 {
1358 __block OSStatus status = errAuthorizationSuccess;
1359 __block bool save_password = false;
1360 __block bool password_only = false;
1361 CFIndex rights_count = auth_rights_get_count(rights);
1362 ccaudit_t ccaudit = NULL;
1363
1364 require(rights != NULL, done);
1365
1366 ccaudit = ccaudit_create(engine->proc, engine->auth, AUE_ssauthorize);
1367 if (auth_rights_get_count(rights) > 0) {
1368 ccaudit_log(ccaudit, "begin evaluation", NULL, 0);
1369 }
1370
1371 if (rights_count) {
1372 __block CFMutableArrayRef rights_list = NULL;
1373 rights_list = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1374 auth_rights_iterate(rights, ^bool(const char *key) {
1375 if (!key)
1376 return true;
1377
1378 CFStringRef tmp = CFStringCreateWithCString(kCFAllocatorDefault, key, kCFStringEncodingUTF8);
1379 if (tmp) {
1380 CFArrayAppendValue(rights_list, tmp);
1381 CFRelease(tmp);
1382 } else {
1383 os_log_error(AUTHD_LOG, "Unable to get details for right %{public}s", key);
1384 }
1385 return true;
1386 });
1387
1388 os_log_info(AUTHD_LOG, "Process %{public}s (PID %d) evaluates %ld rights (engine %lld): %{public}@", process_get_code_url(engine->proc), process_get_pid(engine->proc), (long)rights_count, engine->engine_index, rights_list);
1389 CFReleaseNull(rights_list);
1390 }
1391
1392 if (!auth_token_apple_signed(engine->auth)) {
1393 #ifdef NDEBUG
1394 flags &= ~kAuthorizationFlagSheet;
1395 #else
1396 os_log_debug(AUTHD_LOG, "engine %lld: in release mode, extra flags would be ommited as creator is not signed by Apple", engine->engine_index);
1397 #endif
1398 }
1399
1400 engine->flags = flags;
1401
1402 if (environment) {
1403 _parse_environment(engine, environment);
1404 auth_items_copy(engine->hints, environment);
1405 }
1406
1407 // Restore all context values from the AuthorizationRef
1408 auth_items_t decrypted_items = auth_items_create();
1409 require_action(decrypted_items != NULL, done, os_log_error(AUTHD_LOG, "Unable to create items (engine %lld)", engine->engine_index));
1410 auth_items_content_copy(decrypted_items, auth_token_get_context(engine->auth));
1411 auth_items_decrypt(decrypted_items, auth_token_get_encryption_key(engine->auth));
1412 auth_items_copy(engine->context, decrypted_items);
1413 CFReleaseSafe(decrypted_items);
1414
1415 if (engine->flags & kAuthorizationFlagSheet) {
1416 CFTypeRef extract_password_entitlement = auth_token_copy_entitlement_value(engine->auth, "com.apple.authorization.extract-password");
1417 if (extract_password_entitlement && (CFGetTypeID(extract_password_entitlement) == CFBooleanGetTypeID()) && extract_password_entitlement == kCFBooleanTrue) {
1418 save_password = true;
1419 os_log_debug(AUTHD_LOG, "engine %lld: authorization allowed to extract password", engine->engine_index);
1420 } else {
1421 os_log_debug(AUTHD_LOG, "engine %lld: authorization NOT allowed to extract password", engine->engine_index);
1422 }
1423 CFReleaseSafe(extract_password_entitlement);
1424
1425 // TODO: Remove when all clients have adopted entitlement
1426 if (!enforced_entitlement()) {
1427 save_password = true;
1428 }
1429
1430 // Try to use/update fresh context values from the environment
1431 require_action(environment, done, os_log_error(AUTHD_LOG, "Missing environment for sheet authorization (engine %lld)", engine->engine_index); status = errAuthorizationDenied);
1432
1433 const char *user = auth_items_get_string(environment, kAuthorizationEnvironmentUsername);
1434 require_action(user, done, os_log_error(AUTHD_LOG, "Missing username (engine %lld)", engine->engine_index); status = errAuthorizationDenied);
1435
1436 auth_items_set_string(engine->context, kAuthorizationEnvironmentUsername, user);
1437 struct passwd *pwd = getpwnam(user);
1438 require_action(pwd, done, os_log_error(AUTHD_LOG, "Invalid username %s (engine %lld)", user, engine->engine_index); status = errAuthorizationDenied);
1439 auth_items_set_uint(engine->context, "sheet-uid", pwd->pw_uid);
1440
1441 // move sheet-specific items from hints to context
1442 const char *service = auth_items_get_string(engine->hints, AGENT_CONTEXT_AP_PAM_SERVICE_NAME);
1443 if (service) {
1444 if (auth_items_exist(engine->hints, AGENT_CONTEXT_AP_USER_NAME)) {
1445 auth_items_set_string(engine->context, AGENT_CONTEXT_AP_USER_NAME, auth_items_get_string(engine->hints, AGENT_CONTEXT_AP_USER_NAME));
1446 auth_items_remove(engine->hints, AGENT_CONTEXT_AP_USER_NAME);
1447 } else {
1448 auth_items_set_string(engine->context, AGENT_CONTEXT_AP_USER_NAME, user);
1449 }
1450
1451 auth_items_set_string(engine->context, AGENT_CONTEXT_AP_PAM_SERVICE_NAME, service);
1452 auth_items_remove(engine->hints, AGENT_CONTEXT_AP_PAM_SERVICE_NAME);
1453 }
1454
1455 if (auth_items_exist(environment, AGENT_CONTEXT_AP_TOKEN)) {
1456 size_t datalen = 0;
1457 const void *data = auth_items_get_data(engine->hints, AGENT_CONTEXT_AP_TOKEN, &datalen);
1458 if (data) {
1459 auth_items_set_data(engine->context, AGENT_CONTEXT_AP_TOKEN, data, datalen);
1460 }
1461 auth_items_remove(engine->hints, AGENT_CONTEXT_AP_TOKEN);
1462 }
1463
1464 engine_acquire_sheet_data(engine);
1465 _extract_password_from_la(engine);
1466 engine->preauthorizing = true;
1467 }
1468
1469 engine->dismissed = false;
1470 auth_rights_clear(engine->grantedRights);
1471
1472 if (!(engine->flags & kAuthorizationFlagIgnorePasswordOnly))
1473 {
1474 // first check if any of rights uses rule with password-only set to true
1475 // if so, set appropriate hint so SecurityAgent won't use alternate authentication methods like smartcard etc.
1476 authdb_connection_t dbconn = authdb_connection_acquire(server_get_database()); // get db handle
1477 auth_rights_iterate(rights, ^bool(const char *key) {
1478 if (!key)
1479 return true;
1480 os_log_debug(AUTHD_LOG, "engine %lld: checking if rule %{public}s contains password-only item", engine->engine_index, key);
1481
1482 rule_t rule = _find_rule(engine, dbconn, key);
1483
1484 if (rule && _preevaluate_rule(engine, rule)) {
1485 password_only = true;
1486 CFReleaseSafe(rule);
1487 return false;
1488 }
1489 CFReleaseSafe(rule);
1490 return true;
1491 });
1492 authdb_connection_release(&dbconn); // release db handle
1493 } else {
1494 os_log_info(AUTHD_LOG, "Password-only flag ignored (engine %lld)", engine->engine_index);
1495 }
1496
1497 if (password_only) {
1498 os_log_debug(AUTHD_LOG, "engine %lld: password-only item found, forcing SecurityAgent to use password-only UI", engine->engine_index);
1499 auth_items_set_bool(engine->immutable_hints, AGENT_HINT_PASSWORD_ONLY, true);
1500 }
1501
1502 auth_rights_iterate(rights, ^bool(const char *key) {
1503 if (!key)
1504 return true;
1505
1506
1507 if (!_verify_sandbox(engine, key)) { // _verify_sandbox is already logging failures
1508 status = errAuthorizationDenied;
1509 return false;
1510 }
1511
1512 authdb_connection_t dbconn = authdb_connection_acquire(server_get_database()); // get db handle
1513
1514 os_log_debug(AUTHD_LOG, "engine %lld: evaluate right %{public}s", engine->engine_index, key);
1515 rule_t rule = _find_rule(engine, dbconn, key);
1516 const char * rule_name = rule_get_name(rule);
1517 if (rule_name && (strcasecmp(rule_name, "") == 0)) {
1518 rule_name = "default (not defined)";
1519 }
1520 os_log_debug(AUTHD_LOG, "engine %lld: using rule %{public}s", engine->engine_index, rule_name);
1521
1522 // only need the hints & mechanisms if we are going to show ui
1523 if (engine->flags & kAuthorizationFlagInteractionAllowed) {
1524 _set_right_hints(engine->hints, key);
1525 _set_localization_hints(dbconn, engine->hints, rule);
1526 if (!engine->authenticateRule) {
1527 engine->authenticateRule = rule_create_with_string("authenticate", dbconn);
1528 }
1529 }
1530
1531 authdb_connection_release(&dbconn); // release db handle
1532
1533 engine->currentRightName = key;
1534 engine->currentRule = rule;
1535
1536 ccaudit_log(ccaudit, key, rule_name, 0);
1537
1538 status = _evaluate_rule(engine, engine->currentRule, &save_password);
1539 switch (status) {
1540 case errAuthorizationSuccess:
1541 auth_rights_add(engine->grantedRights, key);
1542 auth_rights_set_flags(engine->grantedRights, key, auth_rights_get_flags(rights,key));
1543
1544 if ((engine->flags & kAuthorizationFlagPreAuthorize) &&
1545 (rule_get_class(engine->currentRule) == RC_USER) &&
1546 (rule_get_timeout(engine->currentRule) == 0)) {
1547 // FIXME: kAuthorizationFlagPreAuthorize => kAuthorizationFlagCanNotPreAuthorize ???
1548 auth_rights_set_flags(engine->grantedRights, engine->currentRightName, kAuthorizationFlagPreAuthorize);
1549 }
1550
1551 os_log(AUTHD_LOG, "Succeeded authorizing right '%{public}s' by client '%{public}s' [%d] for authorization created by '%{public}s' [%d] (%X,%d) (engine %lld)",
1552 key, process_get_code_url(engine->proc), process_get_pid(engine->proc),
1553 auth_token_get_code_url(engine->auth), auth_token_get_pid(engine->auth), (unsigned int)engine->flags, auth_token_least_privileged(engine->auth), engine->engine_index);
1554 break;
1555 case errAuthorizationDenied:
1556 case errAuthorizationInteractionNotAllowed:
1557 case errAuthorizationCanceled:
1558 if (engine->flags & kAuthorizationFlagInteractionAllowed) {
1559 os_log(AUTHD_LOG, "Failed to authorize right '%{public}s' by client '%{public}s' [%d] for authorization created by '%{public}s' [%d] (%X,%d) (%i) (engine %lld)",
1560 key, process_get_code_url(engine->proc), process_get_pid(engine->proc),
1561 auth_token_get_code_url(engine->auth), auth_token_get_pid(engine->auth), (unsigned int)engine->flags, auth_token_least_privileged(engine->auth), (int)status,
1562 engine->engine_index);
1563 } else {
1564 os_log_debug(AUTHD_LOG, "Failed to authorize right '%{public}s' by client '%{public}s' [%d] for authorization created by '%{public}s' [%d] (%X,%d) (%d) (engine %lld)",
1565 key, process_get_code_url(engine->proc), process_get_pid(engine->proc),
1566 auth_token_get_code_url(engine->auth), auth_token_get_pid(engine->auth), (unsigned int)engine->flags, auth_token_least_privileged(engine->auth), (int)status,
1567 engine->engine_index);
1568 }
1569 break;
1570 default:
1571 os_log_error(AUTHD_LOG, "Evaluate returned %d, returning errAuthorizationInternal (engine %lld)", (int)status, engine->engine_index);
1572 status = errAuthorizationInternal;
1573 break;
1574 }
1575
1576 ccaudit_log_authorization(ccaudit, engine->currentRightName, status);
1577
1578 CFReleaseSafe(rule);
1579 engine->currentRightName = NULL;
1580 engine->currentRule = NULL;
1581
1582 auth_items_remove_with_flags(engine->hints, kEngineHintsFlagTemporary);
1583
1584 if (!(engine->flags & kAuthorizationFlagPartialRights) && (status != errAuthorizationSuccess)) {
1585 return false;
1586 }
1587
1588 return true;
1589 });
1590
1591 if (password_only) {
1592 os_log_debug(AUTHD_LOG, "engine %lld: removing password-only flag", engine->engine_index);
1593 auth_items_remove(engine->immutable_hints, AGENT_HINT_PASSWORD_ONLY);
1594 }
1595
1596 if ((engine->flags & kAuthorizationFlagPartialRights) && (auth_rights_get_count(engine->grantedRights) > 0)) {
1597 status = errAuthorizationSuccess;
1598 }
1599
1600 if (engine->dismissed) {
1601 os_log_error(AUTHD_LOG, "Dismissed (engine %lld)", engine->engine_index);
1602 status = errAuthorizationDenied;
1603 }
1604
1605 os_log_debug(AUTHD_LOG, "engine %lld: authorize result: %d", engine->engine_index, (int)status);
1606
1607 if (engine->flags & kAuthorizationFlagSheet) {
1608 engine->preauthorizing = false;
1609 }
1610
1611 if ((engine->flags & kAuthorizationFlagExtendRights) && !(engine->flags & kAuthorizationFlagDestroyRights)) {
1612 _cf_set_iterate(engine->credentials, ^bool(CFTypeRef value) {
1613 credential_t cred = (credential_t)value;
1614 // skip all uid credentials when running in least privileged
1615 if (auth_token_least_privileged(engine->auth) && !credential_is_right(cred))
1616 return true;
1617
1618 session_t session = auth_token_get_session(engine->auth);
1619 auth_token_set_credential(engine->auth, cred);
1620 if (credential_get_shared(cred)) {
1621 session_set_credential(session, cred);
1622 }
1623 if (credential_is_right(cred)) {
1624 os_log_debug(AUTHD_LOG, "engine %lld: adding least privileged %{public}scredential %{public}s to authorization", engine->engine_index, credential_get_shared(cred) ? "shared " : "", credential_get_name(cred));
1625 } else {
1626 os_log_debug(AUTHD_LOG, "engine %lld: adding %{public}scredential %{public}s (%i) to authorization", engine->engine_index, credential_get_shared(cred) ? "shared " : "", credential_get_name(cred), credential_get_uid(cred));
1627 }
1628 return true;
1629 });
1630 }
1631
1632 if (status == errAuthorizationSuccess && save_password) {
1633 auth_items_set_flags(engine->context, kAuthorizationEnvironmentPassword, kAuthorizationContextFlagExtractable);
1634 }
1635
1636 if ((status == errAuthorizationSuccess) || (status == errAuthorizationCanceled)) {
1637 auth_items_t encrypted_items = auth_items_create();
1638 require_action(encrypted_items != NULL, done, os_log_error(AUTHD_LOG, "Unable to create items (engine %lld)", engine->engine_index));
1639 auth_items_content_copy_with_flags(encrypted_items, engine->context, kAuthorizationContextFlagExtractable);
1640 #if DEBUG
1641 os_log_debug(AUTHD_LOG,"engine %lld: ********** Dumping context for encryption **********", engine->engine_index);
1642 os_log_debug(AUTHD_LOG, "%@", encrypted_items);
1643 #endif
1644 auth_items_encrypt(encrypted_items, auth_token_get_encryption_key(engine->auth));
1645 auth_items_copy_with_flags(auth_token_get_context(engine->auth), encrypted_items, kAuthorizationContextFlagExtractable);
1646 os_log_debug(AUTHD_LOG, "engine %lld: encrypted authorization context data", engine->engine_index);
1647 CFReleaseSafe(encrypted_items);
1648 }
1649
1650 if (auth_rights_get_count(rights) > 0) {
1651 ccaudit_log(ccaudit, "end evaluation", NULL, status);
1652 }
1653
1654 #if DEBUG
1655 os_log_debug(AUTHD_LOG, "engine %lld: ********** Dumping auth->credentials **********", engine->engine_index);
1656 auth_token_credentials_iterate(engine->auth, ^bool(credential_t cred) {
1657 os_log_debug(AUTHD_LOG, "%@", cred);
1658 return true;
1659 });
1660 os_log_debug(AUTHD_LOG, "engine %lld: ********** Dumping session->credentials **********", engine->engine_index);
1661 session_credentials_iterate(auth_token_get_session(engine->auth), ^bool(credential_t cred) {
1662 os_log_debug(AUTHD_LOG, "%@", cred);
1663 return true;
1664 });
1665 os_log_debug(AUTHD_LOG, "engine %lld: ********** Dumping engine->context **********", engine->engine_index);
1666 os_log_debug(AUTHD_LOG, "%@", engine->context);
1667 os_log_debug(AUTHD_LOG, "engine %lld: ********** Dumping auth->context **********", engine->engine_index);
1668 os_log_debug(AUTHD_LOG, "%@", engine->auth);
1669 os_log_debug(AUTHD_LOG, "engine %lld: ********** Dumping granted rights **********", engine->engine_index);
1670 os_log_debug(AUTHD_LOG, "%@", engine->grantedRights);
1671 #endif
1672
1673 done:
1674 auth_items_clear(engine->context);
1675 auth_items_clear(engine->sticky_context);
1676 CFReleaseSafe(ccaudit);
1677 CFDictionaryRemoveAllValues(engine->mechanism_agents);
1678
1679 return status;
1680 }
1681
1682 static bool
1683 _wildcard_right_exists(engine_t engine, const char * right)
1684 {
1685 // checks if a wild card right exists
1686 // ex: com.apple. system.
1687 bool exists = false;
1688 rule_t rule = NULL;
1689 authdb_connection_t dbconn = authdb_connection_acquire(server_get_database()); // get db handle
1690 require(dbconn != NULL, done);
1691
1692 rule = _find_rule(engine, dbconn, right);
1693 require(rule != NULL, done);
1694
1695 const char * ruleName = rule_get_name(rule);
1696 require(ruleName != NULL, done);
1697 size_t len = strlen(ruleName);
1698 require(len != 0, done);
1699
1700 if (ruleName[len-1] == '.') {
1701 exists = true;
1702 goto done;
1703 }
1704
1705 done:
1706 authdb_connection_release(&dbconn);
1707 CFReleaseSafe(rule);
1708
1709 return exists;
1710 }
1711
1712 // Validate db right modification
1713
1714 // meta rights are constructed as follows:
1715 // we don't allow setting of wildcard rights, so you can only be more specific
1716 // note that you should never restrict things with a wildcard right without disallowing
1717 // changes to the entire domain. ie.
1718 // system.privilege. -> never
1719 // config.add.system.privilege. -> never
1720 // config.modify.system.privilege. -> never
1721 // config.delete.system.privilege. -> never
1722 // For now we don't allow any configuration of configuration rules
1723 // config.config. -> never
1724
1725 OSStatus engine_verify_modification(engine_t engine, rule_t rule, bool remove, bool force_modify)
1726 {
1727 OSStatus status = errAuthorizationDenied;
1728 auth_rights_t checkRight = NULL;
1729 char buf[BUFSIZ];
1730 memset(buf, 0, sizeof(buf));
1731
1732 const char * right = rule_get_name(rule);
1733 require(right != NULL, done);
1734 size_t len = strlen(right);
1735 require(len != 0, done);
1736
1737 require_action(right[len-1] != '.', done, os_log_error(AUTHD_LOG, "Not allowed to set wild card rules (engine %lld)", engine->engine_index));
1738
1739 if (strncasecmp(right, kConfigRight, strlen(kConfigRight)) == 0) {
1740 // special handling of meta right change:
1741 // config.add. config.modify. config.remove. config.{}.
1742 // check for config.<right> (which always starts with config.config.)
1743 strlcat(buf, kConfigRight, sizeof(buf));
1744 } else {
1745 bool existing = (rule_get_id(rule) != 0) ? true : _wildcard_right_exists(engine, right);
1746 if (!remove) {
1747 if (existing || force_modify) {
1748 strlcat(buf, kAuthorizationConfigRightModify,sizeof(buf));
1749 } else {
1750 strlcat(buf, kAuthorizationConfigRightAdd, sizeof(buf));
1751 }
1752 } else {
1753 if (existing) {
1754 strlcat(buf, kAuthorizationConfigRightRemove, sizeof(buf));
1755 } else {
1756 status = errAuthorizationSuccess;
1757 goto done;
1758 }
1759 }
1760 }
1761
1762 strlcat(buf, right, sizeof(buf));
1763
1764 checkRight = auth_rights_create();
1765 auth_rights_add(checkRight, buf);
1766 status = engine_authorize(engine, checkRight, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights);
1767
1768 done:
1769 os_log_debug(AUTHD_LOG, "engine %lld: authorizing %{public}s for db modification: %d", engine->engine_index, right, (int)status);
1770 CFReleaseSafe(checkRight);
1771 return status;
1772 }
1773
1774 void
1775 _engine_set_credential(engine_t engine, credential_t cred, bool shared)
1776 {
1777 os_log_debug(AUTHD_LOG, "engine %lld: adding %{public}scredential %{public}s (%i) to engine shared: %i", engine->engine_index, credential_get_shared(cred) ? "shared " : "", credential_get_name(cred), credential_get_uid(cred), shared);
1778 CFSetSetValue(engine->credentials, cred);
1779 if (shared) {
1780 credential_t sharedCred = credential_create_with_credential(cred, true);
1781 CFSetSetValue(engine->credentials, sharedCred);
1782 CFReleaseSafe(sharedCred);
1783 }
1784 }
1785
1786 auth_rights_t
1787 engine_get_granted_rights(engine_t engine)
1788 {
1789 return engine->grantedRights;
1790 }
1791
1792 CFAbsoluteTime engine_get_time(engine_t engine)
1793 {
1794 return engine->now;
1795 }
1796
1797 void engine_destroy_agents(engine_t engine)
1798 {
1799 engine->dismissed = true;
1800
1801 _cf_dictionary_iterate(engine->mechanism_agents, ^bool(CFTypeRef key __attribute__((__unused__)), CFTypeRef value) {
1802 os_log_debug(AUTHD_LOG, "engine %lld: Destroying %{public}s", engine->engine_index, mechanism_get_string((mechanism_t)key));
1803 agent_t agent = (agent_t)value;
1804 agent_destroy(agent);
1805
1806 return true;
1807 });
1808 }
1809
1810 void engine_interrupt_agent(engine_t engine)
1811 {
1812 _cf_dictionary_iterate(engine->mechanism_agents, ^bool(CFTypeRef key __attribute__((__unused__)), CFTypeRef value) {
1813 agent_t agent = (agent_t)value;
1814 agent_notify_interrupt(agent);
1815 return true;
1816 });
1817 }
1818
1819 CFTypeRef engine_copy_context(engine_t engine, auth_items_t source)
1820 {
1821 CFTypeRef retval = NULL;
1822
1823 process_t proc = connection_get_process(engine->conn);
1824 if (!proc) {
1825 os_log_error(AUTHD_LOG, "No client process (engine %lld)", engine->engine_index);
1826 return retval;
1827 }
1828
1829 uid_t client_uid = process_get_uid(proc);
1830 if (!client_uid) {
1831 os_log_error(AUTHD_LOG, "No client UID (engine %lld)", engine->engine_index);
1832 return retval;
1833 }
1834
1835 size_t dataLen = 0;
1836 const void *data = auth_items_get_data(source, AGENT_HINT_SHEET_CONTEXT, &dataLen);
1837 if (data) {
1838 CFDataRef externalized = CFDataCreate(kCFAllocatorDefault, data, dataLen);
1839 if (externalized) {
1840 os_log_debug(AUTHD_LOG, "engine %lld: going to get LA context for UID %d", engine->engine_index, client_uid);
1841 retval = LACreateNewContextWithACMContextInSession(client_uid, externalized, NULL);
1842 CFRelease(externalized);
1843 }
1844 }
1845
1846 return retval;
1847 }
1848
1849 bool engine_acquire_sheet_data(engine_t engine)
1850 {
1851 if (!auth_items_exist(engine->context, "sheet-uid"))
1852 return false;
1853
1854 uid_t uid = auth_items_get_uint(engine->context, "sheet-uid");
1855
1856 CFReleaseSafe(engine->la_context);
1857 engine->la_context = engine_copy_context(engine, engine->hints);
1858 if (engine->la_context) {
1859 os_log_debug(AUTHD_LOG, "engine %lld: Sheet UID %d", engine->engine_index, uid);
1860 return true;
1861 } else {
1862 // this is not real failure as no LA context in authorization context is very valid scenario
1863 os_log_debug(AUTHD_LOG, "engine %lld: Failed to get LA context", engine->engine_index);
1864 }
1865 return false;
1866 }