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