]> git.saurik.com Git - apple/security.git/blob - OSX/authd/engine.c
Security-57336.1.9.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
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
421 // check if any agent has been interrupted (it necessary if interrupt will come during creation)
422 CFIndex j;
423 agent_t agent1;
424 for (j = 0; j < i; j++) {
425 agent1 = _get_agent(engine, (mechanism_t)CFArrayGetValueAtIndex(mechanisms, j), false, j == 0);
426 if(agent_get_state(agent1) == interrupting) {
427 break;
428 }
429 }
430 if (j < i) {
431 LOGV("engine[%i]: mechanisms interrupted", connection_get_pid(engine->conn));
432 char * buf = NULL;
433 asprintf(&buf, "evaluation interrupted by %s; restarting evaluation there", mechanism_get_string(agent_get_mechanism(agent1)));
434 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(agent_get_mechanism(agent1)), kAuthorizationResultAllow, buf);
435 free_safe(buf);
436 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(mech), kAuthorizationResultAllow, NULL);
437 const char * token_name = auth_items_get_string(hints, AGENT_HINT_TOKEN_NAME);
438 if (token_name && strlen(token_name) == 0) {
439 auth_items_remove(hints, AGENT_HINT_TOKEN_NAME);
440 }
441 auth_items_copy(context, agent_get_context(agent1));
442 auth_items_copy(hints, agent_get_hints(agent1));
443
444 i = j - 1;
445
446 continue;
447 }
448
449 LOGV("engine[%i]: running mechanism %s (%li of %li)", connection_get_pid(engine->conn), mechanism_get_string(agent_get_mechanism(agent)), i+1, count);
450 result = agent_run(agent, hints, context, engine->immutable_hints);
451
452 auth_items_copy(context, agent_get_context(agent));
453 auth_items_copy(hints, agent_get_hints(agent));
454
455 bool interrupted = false;
456 for (CFIndex i2 = 0; i2 != i; i2++) {
457 agent_t agent2 = _get_agent(engine, (mechanism_t)CFArrayGetValueAtIndex(mechanisms, i2), false, i == 0);
458 if (agent_get_state(agent2) == interrupting) {
459 agent_deactivate(agent);
460 interrupted = true;
461 i = i2 - 1;
462 char * buf = NULL;
463 asprintf(&buf, "evaluation interrupted by %s; restarting evaluation there", mechanism_get_string(agent_get_mechanism(agent2)));
464 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(agent_get_mechanism(agent2)), kAuthorizationResultAllow, buf);
465 free_safe(buf);
466 auth_items_copy(context, agent_get_context(agent2));
467 auth_items_copy(hints, agent_get_hints(agent2));
468 break;
469 }
470 }
471
472 // Empty token name means that token doesn't exist (e.g. SC was removed).
473 // Remove empty token name from hints for UI drawing logic.
474 const char * token_name = auth_items_get_string(hints, AGENT_HINT_TOKEN_NAME);
475 if (token_name && strlen(token_name) == 0) {
476 auth_items_remove(hints, AGENT_HINT_TOKEN_NAME);
477 }
478
479 if (interrupted) {
480 LOGV("engine[%i]: mechanisms interrupted", connection_get_pid(engine->conn));
481 enum Reason reason = worldChanged;
482 auth_items_set_data(hints, AGENT_HINT_RETRY_REASON, &reason, sizeof(reason));
483 result = kAuthorizationResultAllow;
484 _cf_dictionary_iterate(engine->mechanism_agents, ^bool(CFTypeRef key __attribute__((__unused__)), CFTypeRef value) {
485 agent_t tempagent = (agent_t)value;
486 agent_clear_interrupt(tempagent);
487 return true;
488 });
489 }
490 }
491
492 if (result == kAuthorizationResultAllow) {
493 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(mech), kAuthorizationResultAllow, NULL);
494 } else {
495 ccaudit_log_mechanism(ccaudit, engine->currentRightName, mechanism_get_string(mech), (uint32_t)result, NULL);
496 break;
497 }
498 }
499
500 done:
501 if ((result == kAuthorizationResultUserCanceled) || (result == kAuthorizationResultAllow)) {
502 // only make non-sticky context values available externally
503 auth_items_set_flags(context, kAuthorizationEnvironmentPassword, kAuthorizationContextFlagVolatile);
504 // <rdar://problem/16275827> Takauthorizationenvironmentusername should always be extractable
505 auth_items_set_flags(context, kAuthorizationEnvironmentUsername, kAuthorizationContextFlagExtractable);
506 auth_items_copy_with_flags(engine->context, context, kAuthorizationContextFlagExtractable | kAuthorizationContextFlagVolatile);
507 } else if (result == kAuthorizationResultDeny) {
508 auth_items_clear(engine->sticky_context);
509 // save off sticky values in context
510 auth_items_copy_with_flags(engine->sticky_context, context, kAuthorizationContextFlagSticky);
511 }
512
513 CFReleaseSafe(ccaudit);
514 CFReleaseSafe(context);
515 CFReleaseSafe(hints);
516
517 switch(result)
518 {
519 case kAuthorizationResultDeny:
520 return errAuthorizationDenied;
521 case kAuthorizationResultUserCanceled:
522 return errAuthorizationCanceled;
523 case kAuthorizationResultAllow:
524 return errAuthorizationSuccess;
525 case kAuthorizationResultUndefined:
526 return errAuthorizationInternal;
527 default:
528 {
529 LOGV("engine[%i]: unexpected error result", connection_get_pid(engine->conn));
530 return errAuthorizationInternal;
531 }
532 }
533 }
534
535 static OSStatus
536 _evaluate_authentication(engine_t engine, rule_t rule)
537 {
538 OSStatus status = errAuthorizationDenied;
539 ccaudit_t ccaudit = ccaudit_create(engine->proc, engine->auth, AUE_ssauthint);
540 LOGV("engine[%i]: evaluate authentication", connection_get_pid(engine->conn));
541 _set_rule_hints(engine->hints, rule);
542 _set_session_hints(engine, rule);
543
544 CFArrayRef mechanisms = rule_get_mechanisms(rule);
545 if (!(CFArrayGetCount(mechanisms) > 0)) {
546 mechanisms = rule_get_mechanisms(engine->authenticateRule);
547 }
548 require_action(CFArrayGetCount(mechanisms) > 0, done, LOGV("engine[%i]: error no mechanisms found", connection_get_pid(engine->conn)));
549
550 int64_t ruleTries = rule_get_tries(rule);
551 for (engine->tries = 0; engine->tries < ruleTries; engine->tries++) {
552
553 auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason));
554 auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries);
555
556 status = _evaluate_mechanisms(engine, mechanisms);
557
558 LOGV("engine[%i]: evaluate mechanisms result %d", connection_get_pid(engine->conn), (int)status);
559
560 // successfully ran mechanisms to obtain credential
561 if (status == errAuthorizationSuccess) {
562 // deny is the default
563 status = errAuthorizationDenied;
564
565 credential_t newCred = NULL;
566 if (auth_items_exist(engine->context, "uid")) {
567 newCred = credential_create(auth_items_get_uint(engine->context, "uid"));
568 } else {
569 LOGV("engine[%i]: mechanism failed to return a valid uid", connection_get_pid(engine->conn));
570 }
571
572 if (newCred) {
573 if (credential_get_valid(newCred)) {
574 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);
575 ccaudit_log_success(ccaudit, newCred, engine->currentRightName);
576 } else {
577 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);
578 ccaudit_log_failure(ccaudit, auth_items_get_string(engine->context, "username"), engine->currentRightName);
579 }
580
581 status = _evaluate_user_credential_for_rule(engine, newCred, rule, true, false, &engine->reason);
582
583 if (status == errAuthorizationSuccess) {
584 _engine_set_credential(engine, newCred, rule_get_shared(rule));
585 CFReleaseSafe(newCred);
586
587 if (auth_token_least_privileged(engine->auth)) {
588 credential_t rightCred = credential_create_with_right(engine->currentRightName);
589 _engine_set_credential(engine, rightCred, rule_get_shared(rule));
590 CFReleaseSafe(rightCred);
591 }
592
593 session_t session = auth_token_get_session(engine->auth);
594 if (credential_get_uid(newCred) == session_get_uid(session)) {
595 LOGV("engine[%i]: authenticated as the session owner", connection_get_pid(engine->conn));
596 session_set_attributes(auth_token_get_session(engine->auth), AU_SESSION_FLAG_HAS_AUTHENTICATED);
597 }
598
599 break;
600 }
601
602 CFReleaseSafe(newCred);
603 }
604
605 } else if (status == errAuthorizationCanceled || status == errAuthorizationInternal) {
606 break;
607 } else if (status == errAuthorizationDenied) {
608 engine->reason = invalidPassphrase;
609 }
610 }
611
612 if (engine->tries == ruleTries) {
613 engine->reason = tooManyTries;
614 auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason));
615 auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries);
616 _evaluate_mechanisms(engine, mechanisms);
617 ccaudit_log(ccaudit, engine->currentRightName, NULL, 1113);
618 }
619
620 done:
621 CFReleaseSafe(ccaudit);
622
623 return status;
624 }
625
626 static bool
627 _check_entitlement_for_rule(engine_t engine, rule_t rule)
628 {
629 bool entitled = false;
630 CFTypeRef value = NULL;
631
632 if (rule_check_flags(rule, RuleFlagEntitledAndGroup)) {
633 if (auth_token_has_entitlement_for_right(engine->auth, engine->currentRightName)) {
634 if (credential_check_membership(auth_token_get_credential(engine->auth), rule_get_group(rule))) {
635 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));
636 entitled = true;
637 goto done;
638 }
639 }
640 }
641
642 if (rule_check_flags(rule, RuleFlagVPNEntitledAndGroup)) {
643 // com.apple.networking.vpn.configuration is an array we only check for it's existence
644 value = auth_token_copy_entitlement_value(engine->auth, "com.apple.networking.vpn.configuration");
645 if (value) {
646 if (credential_check_membership(auth_token_get_credential(engine->auth), rule_get_group(rule))) {
647 LOGV("engine[%i]: creator of authorization has VPN entitlement and is member of group '%s'", connection_get_pid(engine->conn), rule_get_group(rule));
648 entitled = true;
649 goto done;
650 }
651 }
652 }
653
654 done:
655 CFReleaseSafe(value);
656 return entitled;
657 }
658
659 static OSStatus
660 _evaluate_class_user(engine_t engine, rule_t rule)
661 {
662 __block OSStatus status = errAuthorizationDenied;
663
664 if (_check_entitlement_for_rule(engine,rule)) {
665 return errAuthorizationSuccess;
666 }
667
668 if (rule_get_allow_root(rule) && auth_token_get_uid(engine->auth) == 0) {
669 LOGV("engine[%i]: creator of authorization has uid == 0 granting right %s", connection_get_pid(engine->conn), engine->currentRightName);
670 return errAuthorizationSuccess;
671 }
672
673 if (!rule_get_authenticate_user(rule)) {
674 status = _evaluate_user_credential_for_rule(engine, engine->sessionCredential, rule, true, true, NULL);
675
676 if (status == errAuthorizationSuccess) {
677 return errAuthorizationSuccess;
678 }
679
680 return errAuthorizationDenied;
681 }
682
683 // First -- check all the credentials we have either acquired or currently have
684 _cf_set_iterate(engine->credentials, ^bool(CFTypeRef value) {
685 credential_t cred = (credential_t)value;
686 // Passed-in user credentials are allowed for least-privileged mode
687 if (auth_token_least_privileged(engine->auth) && !credential_is_right(cred) && credential_get_valid(cred)) {
688 status = _evaluate_user_credential_for_rule(engine, cred, rule, false, false, NULL);
689 if (errAuthorizationSuccess == status) {
690 credential_t rightCred = credential_create_with_right(engine->currentRightName);
691 _engine_set_credential(engine,rightCred,rule_get_shared(rule));
692 CFReleaseSafe(rightCred);
693 return false; // exit loop
694 }
695 }
696
697 status = _evaluate_credential_for_rule(engine, cred, rule, false, false, NULL);
698 if (status == errAuthorizationSuccess) {
699 return false; // exit loop
700 }
701 return true;
702 });
703
704 if (status == errAuthorizationSuccess) {
705 return status;
706 }
707
708 // Second -- go through the credentials associated to the authorization token session/auth token
709 _cf_set_iterate(engine->effectiveCredentials, ^bool(CFTypeRef value) {
710 credential_t cred = (credential_t)value;
711 status = _evaluate_credential_for_rule(engine, cred, rule, false, false, NULL);
712 if (status == errAuthorizationSuccess) {
713 // Add the credential we used to the output set.
714 _engine_set_credential(engine, cred, false);
715 return false; // exit loop
716 }
717 return true;
718 });
719
720 if (status == errAuthorizationSuccess) {
721 return status;
722 }
723
724 // Finally - we didn't find a credential. Obtain a new credential if our flags let us do so.
725 if (!(engine->flags & kAuthorizationFlagExtendRights)) {
726 LOGV("engine[%i]: authorization denied (kAuthorizationFlagExtendRights not set)", connection_get_pid(engine->conn));
727 return errAuthorizationDenied;
728 }
729
730 // authorization that timeout immediately cannot be preauthorized
731 if (engine->flags & kAuthorizationFlagPreAuthorize && rule_get_timeout(rule) == 0) {
732 return errAuthorizationSuccess;
733 }
734
735 if (!(engine->flags & kAuthorizationFlagInteractionAllowed)) {
736 LOGV("engine[%i]: Interaction not allowed (kAuthorizationFlagInteractionAllowed not set)", connection_get_pid(engine->conn));
737 return errAuthorizationInteractionNotAllowed;
738 }
739
740 if (!(session_get_attributes(auth_token_get_session(engine->auth)) & AU_SESSION_FLAG_HAS_GRAPHIC_ACCESS)) {
741 LOGV("engine[%i]: Interaction not allowed (session has no ui access)", connection_get_pid(engine->conn));
742 return errAuthorizationInteractionNotAllowed;
743 }
744
745 if (server_in_dark_wake()) {
746 LOGV("engine[%i]: authorization denied (in DarkWake)", connection_get_pid(engine->conn));
747 return errAuthorizationDenied;
748 }
749
750 return _evaluate_authentication(engine, rule);
751 }
752
753 static OSStatus
754 _evaluate_class_rule(engine_t engine, rule_t rule, bool *save_pwd)
755 {
756 __block OSStatus status = errAuthorizationDenied;
757 int64_t kofn = rule_get_kofn(rule);
758
759 uint32_t total = (uint32_t)rule_get_delegates_count(rule);
760 __block uint32_t success_count = 0;
761 __block uint32_t count = 0;
762 LOGV("engine[%i]: ** rule %s has %zi delegates kofn = %lli", connection_get_pid(engine->conn), rule_get_name(rule), total, kofn);
763 rule_delegates_iterator(rule, ^bool(rule_t delegate) {
764 count++;
765
766 if (kofn != 0 && success_count == kofn) {
767 status = errAuthorizationSuccess;
768 return false;
769 }
770
771 LOGV("engine[%i]: * evaluate rule %s (%i)", connection_get_pid(engine->conn), rule_get_name(delegate), count);
772 status = _evaluate_rule(engine, delegate, save_pwd);
773
774 // if status is cancel/internal error abort
775 if ((status == errAuthorizationCanceled) || (status == errAuthorizationInternal))
776 return false;
777
778 if (status != errAuthorizationSuccess) {
779 if (kofn != 0) {
780 // if remaining is less than required abort
781 if ((total - count) < (kofn - success_count)) {
782 LOGD("engine[%i]: rule evaluation remaining: %i, required: %lli", connection_get_pid(engine->conn), (total - count), (kofn - success_count));
783 return false;
784 }
785 return true;
786 }
787 return false;
788 } else {
789 success_count++;
790 return true;
791 }
792 });
793
794 return status;
795 }
796
797 static OSStatus
798 _evaluate_class_mechanism(engine_t engine, rule_t rule)
799 {
800 OSStatus status = errAuthorizationDenied;
801 CFArrayRef mechanisms = NULL;
802
803 require_action(rule_get_mechanisms_count(rule) > 0, done, status = errAuthorizationSuccess; LOGV("engine[%i]: no mechanisms specified", connection_get_pid(engine->conn)));
804
805 mechanisms = rule_get_mechanisms(rule);
806
807 if (server_in_dark_wake()) {
808 CFIndex count = CFArrayGetCount(mechanisms);
809 for (CFIndex i = 0; i < count; i++) {
810 if (!mechanism_is_privileged((mechanism_t)CFArrayGetValueAtIndex(mechanisms, i))) {
811 LOGV("engine[%i]: authorization denied (in DarkWake)", connection_get_pid(engine->conn));
812 goto done;
813 }
814 }
815 }
816
817 int64_t ruleTries = rule_get_tries(rule);
818 engine->tries = 0;
819 do {
820 auth_items_set_data(engine->hints, AGENT_HINT_RETRY_REASON, &engine->reason, sizeof(engine->reason));
821 auth_items_set_int(engine->hints, AGENT_HINT_TRIES, engine->tries);
822
823 status = _evaluate_mechanisms(engine, mechanisms);
824 LOGV("engine[%i]: evaluate mechanisms result %d", connection_get_pid(engine->conn), (int)status);
825
826 if (status == errAuthorizationSuccess) {
827 credential_t newCred = NULL;
828 if (auth_items_exist(engine->context, "uid")) {
829 newCred = credential_create(auth_items_get_uint(engine->context, "uid"));
830 } else {
831 LOGV("engine[%i]: mechanism did not return a uid", connection_get_pid(engine->conn));
832 }
833
834 if (newCred) {
835 _engine_set_credential(engine, newCred, rule_get_shared(rule));
836
837 if (auth_token_least_privileged(engine->auth)) {
838 credential_t rightCred = credential_create_with_right(engine->currentRightName);
839 _engine_set_credential(engine, rightCred, rule_get_shared(rule));
840 CFReleaseSafe(rightCred);
841 }
842
843 if (strcmp(engine->currentRightName, "system.login.console") == 0 && !auth_items_exist(engine->context, AGENT_CONTEXT_AUTO_LOGIN)) {
844 session_set_attributes(auth_token_get_session(engine->auth), AU_SESSION_FLAG_HAS_AUTHENTICATED);
845 }
846
847 CFReleaseSafe(newCred);
848 }
849 }
850
851 engine->tries++;
852
853 } while ( (status == errAuthorizationDenied) // only if we have an expected faulure we continue
854 && ((ruleTries == 0) || ((ruleTries > 0) && engine->tries < ruleTries))); // ruleTries == 0 means we try forever
855 // ruleTires > 0 means we try upto ruleTries times
856 done:
857 return status;
858 }
859
860 static OSStatus
861 _evaluate_rule(engine_t engine, rule_t rule, bool *save_pwd)
862 {
863 if (rule_check_flags(rule, RuleFlagEntitled)) {
864 if (auth_token_has_entitlement_for_right(engine->auth, engine->currentRightName)) {
865 LOGV("engine[%i]: rule allow, creator of authorization has entitlement for right %s", connection_get_pid(engine->conn), engine->currentRightName);
866 return errAuthorizationSuccess;
867 }
868 }
869
870 if (rule_check_flags(rule, RuleFlagRequireAppleSigned)) {
871 if (!auth_token_apple_signed(engine->auth)) {
872 LOGV("engine[%i]: rule deny, creator of authorization is not signed by apple", connection_get_pid(engine->conn));
873 return errAuthorizationDenied;
874 }
875 }
876
877 *save_pwd |= rule_get_extract_password(rule);
878
879 switch (rule_get_class(rule)) {
880 case RC_ALLOW:
881 LOGV("engine[%i]: rule set to allow", connection_get_pid(engine->conn));
882 return errAuthorizationSuccess;
883 case RC_DENY:
884 LOGV("engine[%i]: rule set to deny", connection_get_pid(engine->conn));
885 return errAuthorizationDenied;
886 case RC_USER:
887 return _evaluate_class_user(engine, rule);
888 case RC_RULE:
889 return _evaluate_class_rule(engine, rule, save_pwd);
890 case RC_MECHANISM:
891 return _evaluate_class_mechanism(engine, rule);
892 default:
893 LOGV("engine[%i]: invalid class for rule or rule not found", connection_get_pid(engine->conn));
894 return errAuthorizationInternal;
895 }
896 }
897
898 static rule_t
899 _find_rule(engine_t engine, authdb_connection_t dbconn, const char * string)
900 {
901 rule_t r = NULL;
902 size_t sLen = strlen(string);
903
904 char * buf = calloc(1u, sLen + 1);
905 strlcpy(buf, string, sLen + 1);
906 char * ptr = buf + sLen;
907 __block int64_t count = 0;
908
909 for (;;) {
910
911 // lookup rule
912 authdb_step(dbconn, "SELECT COUNT(name) AS cnt FROM rules WHERE name = ? AND type = 1",
913 ^(sqlite3_stmt *stmt) {
914 sqlite3_bind_text(stmt, 1, buf, -1, NULL);
915 }, ^bool(auth_items_t data) {
916 count = auth_items_get_int64(data, "cnt");
917 return false;
918 });
919
920 if (count > 0) {
921 r = rule_create_with_string(buf, dbconn);
922 goto done;
923 }
924
925 // if buf ends with a . and we didn't find a rule remove .
926 if (*ptr == '.') {
927 *ptr = '\0';
928 }
929 // find any remaining . and truncate the string
930 ptr = strrchr(buf, '.');
931 if (ptr) {
932 *(ptr+1) = '\0';
933 } else {
934 break;
935 }
936 }
937
938 done:
939 free_safe(buf);
940
941 // set default if we didn't find a rule
942 if (r == NULL) {
943 r = rule_create_with_string("", dbconn);
944 if (rule_get_id(r) == 0) {
945 CFReleaseNull(r);
946 LOGE("engine[%i]: default rule lookup error (missing), using builtin defaults", connection_get_pid(engine->conn));
947 r = rule_create_default();
948 }
949 }
950 return r;
951 }
952
953 static void _parse_environment(engine_t engine, auth_items_t environment)
954 {
955 require(environment != NULL, done);
956
957 #if DEBUG
958 LOGV("engine[%i]: Dumping Environment", connection_get_pid(engine->conn));
959 _show_cf(environment);
960 #endif
961
962 // Check if a credential was passed into the environment and we were asked to extend the rights
963 if (engine->flags & kAuthorizationFlagExtendRights) {
964 const char * user = auth_items_get_string(environment, kAuthorizationEnvironmentUsername);
965 const char * pass = auth_items_get_string(environment, kAuthorizationEnvironmentPassword);
966 bool shared = auth_items_exist(environment, kAuthorizationEnvironmentShared);
967 require(user != NULL, done);
968
969 struct passwd *pw = getpwnam(user);
970 require_action(pw != NULL, done, LOGE("engine[%i]: user not found %s", connection_get_pid(engine->conn), user));
971
972 int checkpw_status = checkpw_internal(pw, pass ? pass : "");
973 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));
974
975 credential_t cred = credential_create(pw->pw_uid);
976 if (credential_get_valid(cred)) {
977 LOG("engine[%i]: checkpw() succeeded, creating credential for user %s", connection_get_pid(engine->conn), user);
978 _engine_set_credential(engine, cred, shared);
979
980 auth_items_set_string(engine->context, kAuthorizationEnvironmentUsername, user);
981 auth_items_set_string(engine->context, kAuthorizationEnvironmentPassword, pass ? pass : "");
982 }
983 CFReleaseSafe(cred);
984 }
985
986 done:
987 endpwent();
988 return;
989 }
990
991 static bool _verify_sandbox(engine_t engine, const char * right)
992 {
993 pid_t pid = process_get_pid(engine->proc);
994 if (sandbox_check(pid, "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME, right)) {
995 LOGE("Sandbox denied authorizing right '%s' by client '%s' [%d]", right, process_get_code_url(engine->proc), pid);
996 return false;
997 }
998
999 pid = auth_token_get_pid(engine->auth);
1000 if (auth_token_get_sandboxed(engine->auth) && sandbox_check(pid, "authorization-right-obtain", SANDBOX_FILTER_RIGHT_NAME, right)) {
1001 LOGE("Sandbox denied authorizing right '%s' for authorization created by '%s' [%d]", right, auth_token_get_code_url(engine->auth), pid);
1002 return false;
1003 }
1004
1005 return true;
1006 }
1007
1008 #pragma mark -
1009 #pragma mark engine methods
1010
1011 OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t environment, AuthorizationFlags flags)
1012 {
1013 __block OSStatus status = errAuthorizationSuccess;
1014 __block bool savePassword = false;
1015 ccaudit_t ccaudit = NULL;
1016
1017 require(rights != NULL, done);
1018
1019 ccaudit = ccaudit_create(engine->proc, engine->auth, AUE_ssauthorize);
1020 if (auth_rights_get_count(rights) > 0) {
1021 ccaudit_log(ccaudit, "begin evaluation", NULL, 0);
1022 }
1023
1024 engine->flags = flags;
1025
1026 if (environment) {
1027 _parse_environment(engine, environment);
1028 auth_items_copy(engine->hints, environment);
1029 }
1030
1031 auth_items_copy(engine->context, auth_token_get_context(engine->auth));
1032
1033 engine->dismissed = false;
1034 auth_rights_clear(engine->grantedRights);
1035
1036 auth_rights_iterate(rights, ^bool(const char *key) {
1037 if (!key)
1038 return true;
1039
1040
1041 if (!_verify_sandbox(engine, key)) {
1042 status = errAuthorizationDenied;
1043 return false;
1044 }
1045
1046 authdb_connection_t dbconn = authdb_connection_acquire(server_get_database()); // get db handle
1047
1048 LOGV("engine[%i]: evaluate right %s", connection_get_pid(engine->conn), key);
1049 rule_t rule = _find_rule(engine, dbconn, key);
1050 const char * rule_name = rule_get_name(rule);
1051 if (rule_name && (strcasecmp(rule_name, "") == 0)) {
1052 rule_name = "default (not defined)";
1053 }
1054 LOGV("engine[%i]: using rule %s", connection_get_pid(engine->conn), rule_name);
1055
1056 // only need the hints & mechanisms if we are going to show ui
1057 if (engine->flags & kAuthorizationFlagInteractionAllowed) {
1058 _set_right_hints(engine->hints, key);
1059 _set_localization_hints(dbconn, engine->hints, rule);
1060 if (!engine->authenticateRule) {
1061 engine->authenticateRule = rule_create_with_string("authenticate", dbconn);
1062 }
1063 }
1064
1065 authdb_connection_release(&dbconn); // release db handle
1066
1067 engine->currentRightName = key;
1068 engine->currentRule = rule;
1069
1070 ccaudit_log(ccaudit, key, rule_name, 0);
1071
1072 status = _evaluate_rule(engine, engine->currentRule, &savePassword);
1073 switch (status) {
1074 case errAuthorizationSuccess:
1075 auth_rights_add(engine->grantedRights, key);
1076 auth_rights_set_flags(engine->grantedRights, key, auth_rights_get_flags(rights,key));
1077
1078 if ((engine->flags & kAuthorizationFlagPreAuthorize) &&
1079 (rule_get_class(engine->currentRule) == RC_USER) &&
1080 (rule_get_timeout(engine->currentRule) == 0)) {
1081 // FIXME: kAuthorizationFlagPreAuthorize => kAuthorizationFlagCanNotPreAuthorize ???
1082 auth_rights_set_flags(engine->grantedRights, engine->currentRightName, kAuthorizationFlagPreAuthorize);
1083 }
1084
1085 LOG("Succeeded authorizing right '%s' by client '%s' [%d] for authorization created by '%s' [%d] (%X,%d)",
1086 key, process_get_code_url(engine->proc), process_get_pid(engine->proc),
1087 auth_token_get_code_url(engine->auth), auth_token_get_pid(engine->auth), (unsigned int)engine->flags, auth_token_least_privileged(engine->auth));
1088 break;
1089 case errAuthorizationDenied:
1090 case errAuthorizationInteractionNotAllowed:
1091 case errAuthorizationCanceled:
1092 if (engine->flags & kAuthorizationFlagInteractionAllowed) {
1093 LOG("Failed to authorize right '%s' by client '%s' [%d] for authorization created by '%s' [%d] (%X,%d) (%i)",
1094 key, process_get_code_url(engine->proc), process_get_pid(engine->proc),
1095 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);
1096 } else {
1097 LOGV("Failed to authorize right '%s' by client '%s' [%d] for authorization created by '%s' [%d] (%X,%d) (%d)",
1098 key, process_get_code_url(engine->proc), process_get_pid(engine->proc),
1099 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);
1100 }
1101 break;
1102 default:
1103 LOGE("engine[%i]: evaluate returned %d returning errAuthorizationInternal", connection_get_pid(engine->conn), (int)status);
1104 status = errAuthorizationInternal;
1105 break;
1106 }
1107
1108 ccaudit_log_authorization(ccaudit, engine->currentRightName, status);
1109
1110 CFReleaseSafe(rule);
1111 engine->currentRightName = NULL;
1112 engine->currentRule = NULL;
1113
1114 auth_items_remove_with_flags(engine->hints, kEngineHintsFlagTemporary);
1115
1116 if (!(engine->flags & kAuthorizationFlagPartialRights) && (status != errAuthorizationSuccess)) {
1117 return false;
1118 }
1119
1120 return true;
1121 });
1122
1123 if ((engine->flags & kAuthorizationFlagPartialRights) && (auth_rights_get_count(engine->grantedRights) > 0)) {
1124 status = errAuthorizationSuccess;
1125 }
1126
1127 if (engine->dismissed) {
1128 status = errAuthorizationDenied;
1129 }
1130
1131 LOGV("engine[%i]: authorize result: %d", connection_get_pid(engine->conn), (int)status);
1132
1133 if ((engine->flags & kAuthorizationFlagExtendRights) && !(engine->flags & kAuthorizationFlagDestroyRights)) {
1134 _cf_set_iterate(engine->credentials, ^bool(CFTypeRef value) {
1135 credential_t cred = (credential_t)value;
1136 // skip all uid credentials when running in least privileged
1137 if (auth_token_least_privileged(engine->auth) && !credential_is_right(cred))
1138 return true;
1139
1140 session_t session = auth_token_get_session(engine->auth);
1141 auth_token_set_credential(engine->auth, cred);
1142 if (credential_get_shared(cred)) {
1143 session_set_credential(session, cred);
1144 }
1145 if (credential_is_right(cred)) {
1146 LOGV("engine[%i]: adding least privileged %scredential %s to authorization", connection_get_pid(engine->conn), credential_get_shared(cred) ? "shared " : "", credential_get_name(cred));
1147 } else {
1148 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));
1149 }
1150 return true;
1151 });
1152 }
1153
1154 if (status == errAuthorizationSuccess && savePassword) {
1155 auth_items_set_flags(engine->context, kAuthorizationEnvironmentPassword, kAuthorizationContextFlagExtractable);
1156 }
1157
1158 if ((status == errAuthorizationSuccess) || (status == errAuthorizationCanceled)) {
1159 auth_items_copy_with_flags(auth_token_get_context(engine->auth), engine->context, kAuthorizationContextFlagExtractable);
1160 }
1161
1162 if (auth_rights_get_count(rights) > 0) {
1163 ccaudit_log(ccaudit, "end evaluation", NULL, status);
1164 }
1165
1166 #if DEBUG
1167 LOGV("engine[%i]: ********** Dumping auth->credentials **********", connection_get_pid(engine->conn));
1168 auth_token_credentials_iterate(engine->auth, ^bool(credential_t cred) {
1169 _show_cf(cred);
1170 return true;
1171 });
1172 LOGV("engine[%i]: ********** Dumping session->credentials **********", connection_get_pid(engine->conn));
1173 session_credentials_iterate(auth_token_get_session(engine->auth), ^bool(credential_t cred) {
1174 _show_cf(cred);
1175 return true;
1176 });
1177 LOGV("engine[%i]: ********** Dumping engine->context **********", connection_get_pid(engine->conn));
1178 _show_cf(engine->context);
1179 LOGV("engine[%i]: ********** Dumping auth->context **********", connection_get_pid(engine->conn));
1180 _show_cf(auth_token_get_context(engine->auth));
1181 LOGV("engine[%i]: ********** Dumping granted rights **********", connection_get_pid(engine->conn));
1182 _show_cf(engine->grantedRights);
1183 #endif
1184
1185 done:
1186 auth_items_clear(engine->context);
1187 auth_items_clear(engine->sticky_context);
1188 CFReleaseSafe(ccaudit);
1189 CFDictionaryRemoveAllValues(engine->mechanism_agents);
1190
1191 return status;
1192 }
1193
1194 static bool
1195 _wildcard_right_exists(engine_t engine, const char * right)
1196 {
1197 // checks if a wild card right exists
1198 // ex: com.apple. system.
1199 bool exists = false;
1200 rule_t rule = NULL;
1201 authdb_connection_t dbconn = authdb_connection_acquire(server_get_database()); // get db handle
1202 require(dbconn != NULL, done);
1203
1204 rule = _find_rule(engine, dbconn, right);
1205 require(rule != NULL, done);
1206
1207 const char * ruleName = rule_get_name(rule);
1208 require(ruleName != NULL, done);
1209 size_t len = strlen(ruleName);
1210 require(len != 0, done);
1211
1212 if (ruleName[len-1] == '.') {
1213 exists = true;
1214 goto done;
1215 }
1216
1217 done:
1218 authdb_connection_release(&dbconn);
1219 CFReleaseSafe(rule);
1220
1221 return exists;
1222 }
1223
1224 // Validate db right modification
1225
1226 // meta rights are constructed as follows:
1227 // we don't allow setting of wildcard rights, so you can only be more specific
1228 // note that you should never restrict things with a wildcard right without disallowing
1229 // changes to the entire domain. ie.
1230 // system.privilege. -> never
1231 // config.add.system.privilege. -> never
1232 // config.modify.system.privilege. -> never
1233 // config.delete.system.privilege. -> never
1234 // For now we don't allow any configuration of configuration rules
1235 // config.config. -> never
1236
1237 OSStatus engine_verify_modification(engine_t engine, rule_t rule, bool remove, bool force_modify)
1238 {
1239 OSStatus status = errAuthorizationDenied;
1240 auth_rights_t checkRight = NULL;
1241 char buf[BUFSIZ];
1242 memset(buf, 0, sizeof(buf));
1243
1244 const char * right = rule_get_name(rule);
1245 require(right != NULL, done);
1246 size_t len = strlen(right);
1247 require(len != 0, done);
1248
1249 require_action(right[len-1] != '.', done, LOGE("engine[%i]: not allowed to set wild card rules", connection_get_pid(engine->conn)));
1250
1251 if (strncasecmp(right, kConfigRight, strlen(kConfigRight)) == 0) {
1252 // special handling of meta right change:
1253 // config.add. config.modify. config.remove. config.{}.
1254 // check for config.<right> (which always starts with config.config.)
1255 strlcat(buf, kConfigRight, sizeof(buf));
1256 } else {
1257 bool existing = (rule_get_id(rule) != 0) ? true : _wildcard_right_exists(engine, right);
1258 if (!remove) {
1259 if (existing || force_modify) {
1260 strlcat(buf, kAuthorizationConfigRightModify,sizeof(buf));
1261 } else {
1262 strlcat(buf, kAuthorizationConfigRightAdd, sizeof(buf));
1263 }
1264 } else {
1265 if (existing) {
1266 strlcat(buf, kAuthorizationConfigRightRemove, sizeof(buf));
1267 } else {
1268 status = errAuthorizationSuccess;
1269 goto done;
1270 }
1271 }
1272 }
1273
1274 strlcat(buf, right, sizeof(buf));
1275
1276 checkRight = auth_rights_create();
1277 auth_rights_add(checkRight, buf);
1278 status = engine_authorize(engine, checkRight, NULL, kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights);
1279
1280 done:
1281 LOGV("engine[%i]: authorizing %s for db modification: %d", connection_get_pid(engine->conn), right, (int)status);
1282 CFReleaseSafe(checkRight);
1283 return status;
1284 }
1285
1286 void
1287 _engine_set_credential(engine_t engine, credential_t cred, bool shared)
1288 {
1289 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);
1290 CFSetSetValue(engine->credentials, cred);
1291 if (shared) {
1292 credential_t sharedCred = credential_create_with_credential(cred, true);
1293 CFSetSetValue(engine->credentials, sharedCred);
1294 CFReleaseSafe(sharedCred);
1295 }
1296 }
1297
1298 auth_rights_t
1299 engine_get_granted_rights(engine_t engine)
1300 {
1301 return engine->grantedRights;
1302 }
1303
1304 CFAbsoluteTime engine_get_time(engine_t engine)
1305 {
1306 return engine->now;
1307 }
1308
1309 void engine_destroy_agents(engine_t engine)
1310 {
1311 engine->dismissed = true;
1312
1313 _cf_dictionary_iterate(engine->mechanism_agents, ^bool(CFTypeRef key __attribute__((__unused__)), CFTypeRef value) {
1314 LOGD("engine[%i]: Destroying %s", connection_get_pid(engine->conn), mechanism_get_string((mechanism_t)key));
1315 agent_t agent = (agent_t)value;
1316 agent_destroy(agent);
1317
1318 return true;
1319 });
1320 }
1321
1322 void engine_interrupt_agent(engine_t engine)
1323 {
1324 _cf_dictionary_iterate(engine->mechanism_agents, ^bool(CFTypeRef key __attribute__((__unused__)), CFTypeRef value) {
1325 agent_t agent = (agent_t)value;
1326 agent_notify_interrupt(agent);
1327 return true;
1328 });
1329 }