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