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