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