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