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>
20int 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
27static void _set_process_hints(auth_items_t, process_t);
28static void _set_process_immutable_hints(auth_items_t, process_t);
29static void _set_auth_token_hints(auth_items_t, auth_token_t);
30static OSStatus _evaluate_user_credential_for_rule(engine_t, credential_t, rule_t, bool, bool, enum Reason *);
31static void _engine_set_credential(engine_t, credential_t, bool);
32static OSStatus _evaluate_rule(engine_t, rule_t, bool *);
33
34enum {
35    kEngineHintsFlagTemporary = (1 << 30)
36};
37
38#pragma mark -
39#pragma mark engine creation
40
41struct _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
76static 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
95AUTH_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
105static 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
116engine_t
117engine_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
164done:
165    return engine;
166}
167
168#pragma mark -
169#pragma mark agent hints
170
171void
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
181void
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
188void
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
197static 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
203static 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
215static 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
241static 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
263static 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
286static 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
365static 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
379static 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
398static 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
500done:
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
535static 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 %i", connection_get_pid(engine->conn), 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
620done:
621    CFReleaseSafe(ccaudit);
622
623    return status;
624}
625
626static 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
654done:
655    CFReleaseSafe(value);
656    return entitled;
657}
658
659static 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
753static 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
797static 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 %i", connection_get_pid(engine->conn), 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
856done:
857    return status;
858}
859
860static 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
898static 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
938done:
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
953static void _parse_enviroment(engine_t engine, auth_items_t enviroment)
954{
955    require(enviroment != NULL, done);
956
957#if DEBUG
958    LOGV("engine[%i]: Dumping Enviroment", connection_get_pid(engine->conn));
959    _show_cf(enviroment);
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(enviroment, kAuthorizationEnvironmentUsername);
965        const char * pass = auth_items_get_string(enviroment, kAuthorizationEnvironmentPassword);
966        bool shared = auth_items_exist(enviroment, 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
986done:
987    endpwent();
988    return;
989}
990
991static 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
1011OSStatus engine_authorize(engine_t engine, auth_rights_t rights, auth_items_t enviroment, 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 (enviroment) {
1027        _parse_enviroment(engine, enviroment);
1028        auth_items_copy(engine->hints, enviroment);
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), 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), engine->flags, auth_token_least_privileged(engine->auth), status);
1096                } else {
1097                    LOGV("Failed to authorize right '%s' by client '%s' [%d] for authorization created by '%s' [%d] (%X,%d) (%i)",
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), engine->flags, auth_token_least_privileged(engine->auth), status);
1100                }
1101                break;
1102            default:
1103                LOGE("engine[%i]: evaluate returned %i returning errAuthorizationInternal", connection_get_pid(engine->conn), 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: %i", connection_get_pid(engine->conn), 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
1185done:
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
1194static 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
1217done:
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
1237OSStatus 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
1280done:
1281    LOGV("engine[%i]: authorizing %s for db modification: %i", connection_get_pid(engine->conn), right, status);
1282    CFReleaseSafe(checkRight);
1283    return status;
1284}
1285
1286void
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
1298auth_rights_t
1299engine_get_granted_rights(engine_t engine)
1300{
1301    return engine->grantedRights;
1302}
1303
1304CFAbsoluteTime engine_get_time(engine_t engine)
1305{
1306    return engine->now;
1307}
1308
1309void 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
1322void 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}
1330