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