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