1/* Copyright (c) 2012 Apple Inc. All rights reserved. */
2
3#include "server.h"
4#include "session.h"
5#include "process.h"
6#include "authtoken.h"
7#include "authdb.h"
8#include "rule.h"
9#include "authutilities.h"
10#include "crc.h"
11#include "mechanism.h"
12#include "agent.h"
13#include "authitems.h"
14#include "debugging.h"
15#include "engine.h"
16#include "connection.h"
17
18#include <bsm/libbsm.h>
19#include <Security/Authorization.h>
20#include <Security/AuthorizationPriv.h>
21#include <Security/AuthorizationTagsPriv.h>
22#include <xpc/private.h>
23#include <dispatch/dispatch.h>
24#include <CoreFoundation/CoreFoundation.h>
25#include <CoreFoundation/CFXPCBridge.h>
26#include <IOKit/IOMessage.h>
27#include <IOKit/pwr_mgt/IOPMLib.h>
28#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
29
30#define MAX_PROCESS_RIGHTS   30
31
32static CFMutableDictionaryRef gProcessMap = NULL;
33static CFMutableDictionaryRef gSessionMap = NULL;
34static CFMutableDictionaryRef gAuthTokenMap = NULL;
35static authdb_t gDatabase = NULL;
36
37static dispatch_queue_t power_queue;
38static bool gInDarkWake = false;
39static IOPMConnection gIOPMconn = NULL;
40static bool gXPCTransaction = false;
41
42static dispatch_queue_t
43get_server_dispatch_queue()
44{
45    static dispatch_once_t onceToken;
46    static dispatch_queue_t server_queue = NULL;
47
48    dispatch_once(&onceToken, ^{
49        server_queue = dispatch_queue_create("com.apple.security.auth.server", DISPATCH_QUEUE_SERIAL);
50        check(server_queue != NULL);
51    });
52
53    return server_queue;
54}
55
56static Boolean _processEqualCallBack(const void *value1, const void *value2)
57{
58    audit_info_s * info1 = (audit_info_s*)value1;
59    audit_info_s * info2 = (audit_info_s*)value2;
60    if (info1->pid == info2->pid) {
61        if (info1->tid == info2->tid) {
62            return true;
63        }
64    }
65    return false;
66}
67
68static CFHashCode _processHashCallBack(const void *value)
69{
70    audit_info_s * info = (audit_info_s*)value;
71    uint64_t crc = crc64_init();
72    crc = crc64_update(crc, &info->pid, sizeof(info->pid));
73    crc = crc64_update(crc, &info->tid, sizeof(info->tid));
74    crc = crc64_final(crc);
75    return crc;
76}
77
78static const CFDictionaryKeyCallBacks kProcessMapKeyCallBacks = {
79    .version = 0,
80    .retain = NULL,
81    .release = NULL,
82    .copyDescription = NULL,
83    .equal = &_processEqualCallBack,
84    .hash = &_processHashCallBack
85};
86
87static Boolean _sessionEqualCallBack(const void *value1, const void *value2)
88{
89    return (*(session_id_t*)value1) == (*(session_id_t*)value2);
90}
91
92static CFHashCode _sessionHashCallBack(const void *value)
93{
94    return (CFHashCode)(*(session_id_t*)(value));
95}
96
97static const CFDictionaryKeyCallBacks kSessionMapKeyCallBacks = {
98    .version = 0,
99    .retain = NULL,
100    .release = NULL,
101    .copyDescription = NULL,
102    .equal = &_sessionEqualCallBack,
103    .hash = &_sessionHashCallBack
104};
105
106void server_cleanup()
107{
108    CFRelease(gProcessMap);
109    CFRelease(gSessionMap);
110    CFRelease(gAuthTokenMap);
111
112    IOPMConnectionSetDispatchQueue(gIOPMconn, NULL);
113    IOPMConnectionRelease(gIOPMconn);
114
115    dispatch_queue_t queue = get_server_dispatch_queue();
116    if (queue) {
117        dispatch_release(queue);
118    }
119    dispatch_release(power_queue);
120}
121
122static void _IOMPCallBack(void * param AUTH_UNUSED, IOPMConnection connection, IOPMConnectionMessageToken token, IOPMSystemPowerStateCapabilities capabilities)
123{
124    LOGV("server: IOMP powerstates %i", capabilities);
125    if (capabilities & kIOPMSystemPowerStateCapabilityDisk)
126        LOGV("server: disk");
127    if (capabilities & kIOPMSystemPowerStateCapabilityNetwork)
128        LOGV("server: net");
129    if (capabilities & kIOPMSystemPowerStateCapabilityAudio)
130        LOGV("server: audio");
131    if (capabilities & kIOPMSystemPowerStateCapabilityVideo)
132        LOGV("server: video");
133
134    /* if cpu and no display -> in DarkWake */
135    LOGD("server: DarkWake check current=%i==%i", (capabilities & (kIOPMSystemPowerStateCapabilityCPU|kIOPMSystemPowerStateCapabilityVideo)), kIOPMSystemPowerStateCapabilityCPU);
136    if ((capabilities & (kIOPMSystemPowerStateCapabilityCPU|kIOPMSystemPowerStateCapabilityVideo)) == kIOPMSystemPowerStateCapabilityCPU) {
137        LOGV("server: enter DarkWake");
138        gInDarkWake = true;
139    } else if (gInDarkWake) {
140        LOGV("server: exit DarkWake");
141        gInDarkWake = false;
142    }
143
144    (void)IOPMConnectionAcknowledgeEvent(connection, token);
145
146    return;
147}
148
149static void
150_setupDarkWake(void *ctx)
151{
152    IOReturn ret;
153
154    IOPMConnectionCreate(CFSTR("IOPowerWatcher"),
155                         kIOPMSystemPowerStateCapabilityDisk
156                         | kIOPMSystemPowerStateCapabilityNetwork
157                         | kIOPMSystemPowerStateCapabilityAudio
158                         | kIOPMSystemPowerStateCapabilityVideo,
159                         &gIOPMconn);
160
161    ret = IOPMConnectionSetNotification(gIOPMconn, NULL, _IOMPCallBack);
162    if (ret != kIOReturnSuccess)
163        return;
164
165    IOPMConnectionSetDispatchQueue(gIOPMconn, power_queue);
166
167    IOPMScheduleUserActiveChangedNotification(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(bool active) {
168        if (active) {
169            gInDarkWake = false;
170        }
171    });
172}
173
174bool server_in_dark_wake()
175{
176    return gInDarkWake;
177}
178
179authdb_t server_get_database()
180{
181    return gDatabase;
182}
183
184static void _setupAuditSessionMonitor()
185{
186    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
187        au_sdev_handle_t *dev = au_sdev_open(AU_SDEVF_ALLSESSIONS);
188        int event;
189        auditinfo_addr_t aia;
190
191        if (NULL == dev) {
192            LOGE("server: could not open %s %d", AUDIT_SDEV_PATH, errno);
193            return;
194        }
195
196        for (;;) {
197            if (0 != au_sdev_read_aia(dev, &event, &aia)) {
198                LOGE("server: au_sdev_read_aia failed: %d", errno);
199                continue;
200            }
201            LOGD("server: au_sdev_handle_t event=%i, session=%i", event, aia.ai_asid);
202            if (event == AUE_SESSION_CLOSE) {
203                dispatch_async(get_server_dispatch_queue(), ^{
204                    LOGV("server: session %i destroyed", aia.ai_asid);
205                    CFDictionaryRemoveValue(gSessionMap, &aia.ai_asid);
206                });
207            }
208        }
209
210    });
211}
212
213static void _setupSignalHandlers()
214{
215    signal(SIGTERM, SIG_IGN);
216    static dispatch_source_t sigtermHandler;
217    sigtermHandler = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0, get_server_dispatch_queue());
218    if (sigtermHandler) {
219        dispatch_source_set_event_handler(sigtermHandler, ^{
220
221            // should we clean up any state?
222            exit(EXIT_SUCCESS);
223        });
224        dispatch_resume(sigtermHandler);
225    }
226}
227
228OSStatus server_init(void)
229{
230    OSStatus status = errAuthorizationSuccess;
231
232    auditinfo_addr_t info;
233    memset(&info, 0, sizeof(info));
234    getaudit_addr(&info, sizeof(info));
235    LOGV("server: uid=%i, sid=%i", info.ai_auid, info.ai_asid);
236
237    require_action(get_server_dispatch_queue() != NULL, done, status = errAuthorizationInternal);
238
239    gProcessMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kProcessMapKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
240    require_action(gProcessMap != NULL, done, status = errAuthorizationInternal);
241
242    gSessionMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kSessionMapKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
243    require_action(gSessionMap != NULL, done, status = errAuthorizationInternal);
244
245    gAuthTokenMap = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kAuthTokenKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
246    require_action(gAuthTokenMap != NULL, done, status = errAuthorizationInternal);
247
248    gDatabase = authdb_create();
249    require_action(gDatabase != NULL, done, status = errAuthorizationInternal);
250
251    // check to see if we have an updates
252    authdb_connection_t dbconn = authdb_connection_acquire(gDatabase);
253    authdb_maintenance(dbconn);
254    authdb_connection_release(&dbconn);
255
256    power_queue = dispatch_queue_create("com.apple.security.auth.power", DISPATCH_QUEUE_SERIAL);
257    check(power_queue != NULL);
258    dispatch_async_f(power_queue, NULL, _setupDarkWake);
259
260    _setupAuditSessionMonitor();
261    _setupSignalHandlers();
262
263done:
264    return status;
265}
266
267static void _server_parse_audit_token(audit_token_t * token, audit_info_s * info)
268{
269    if (token && info) {
270        memset(info, 0, sizeof(*info));
271        au_tid_t tid;
272        memset(&tid, 0, sizeof(tid));
273        audit_token_to_au32(*token, &info->auid, &info->euid,
274                            &info->egid, &info->ruid, &info->rgid,
275                            &info->pid, &info->asid, &tid);
276        info->tid = tid.port;
277        info->opaqueToken = *token;
278    }
279}
280
281connection_t
282server_register_connection(xpc_connection_t connection)
283{
284    __block connection_t conn = NULL;
285    __block session_t session = NULL;
286    __block process_t proc = NULL;
287    __block CFIndex conn_count = 0;
288
289    require(connection != NULL, done);
290
291    audit_token_t auditToken;
292    audit_info_s info;
293    xpc_connection_get_audit_token(connection, &auditToken);
294    _server_parse_audit_token(&auditToken, &info);
295
296
297    dispatch_sync(get_server_dispatch_queue(), ^{
298        session = (session_t)CFDictionaryGetValue(gSessionMap, &info.asid);
299        if (session) {
300            CFRetain(session);
301        } else {
302            session = session_create(info.asid);
303            CFDictionarySetValue(gSessionMap, session_get_key(session), session);
304        }
305
306        proc = (process_t)CFDictionaryGetValue(gProcessMap, &info);
307        if (proc) {
308            CFRetain(proc);
309        }
310
311        if (proc) {
312            conn = connection_create(proc);
313            conn_count = process_add_connection(proc, conn);
314        } else {
315            proc = process_create(&info, session);
316            if (proc) {
317                conn = connection_create(proc);
318                conn_count = process_add_connection(proc, conn);
319                session_add_process(session, proc);
320                CFDictionarySetValue(gProcessMap, process_get_key(proc), proc);
321            }
322        }
323
324        if (!gXPCTransaction) {
325            xpc_transaction_begin();
326            gXPCTransaction = true;
327        }
328    });
329
330    LOGV("server[%i]: registered connection (total=%li)", info.pid, conn_count);
331
332done:
333    CFReleaseSafe(session);
334    CFReleaseSafe(proc);
335    return conn;
336}
337
338void
339server_unregister_connection(connection_t conn)
340{
341    if (conn != NULL) {
342        process_t proc = connection_get_process(conn);
343
344        dispatch_sync(get_server_dispatch_queue(), ^{
345            CFIndex connectionCount = process_get_connection_count(proc);
346            LOGV("server[%i]: unregistered connection (total=%li)", process_get_pid(proc), connectionCount);
347
348            if (connectionCount == 1) {
349                CFDictionaryRemoveValue(gProcessMap, process_get_key(proc));
350            }
351
352            if (CFDictionaryGetCount(gProcessMap) == 0) {
353                xpc_transaction_end();
354                gXPCTransaction = false;
355            }
356        });
357        // move the destruction of the connection/process off the server queue
358        CFRelease(conn);
359    }
360}
361
362void
363server_register_auth_token(auth_token_t auth)
364{
365    if (auth != NULL) {
366        dispatch_sync(get_server_dispatch_queue(), ^{
367            LOGV("server: registering auth %p", auth);
368            CFDictionarySetValue(gAuthTokenMap, auth_token_get_key(auth), auth);
369            auth_token_set_state(auth, auth_token_state_registered);
370        });
371    }
372}
373
374void
375server_unregister_auth_token(auth_token_t auth)
376{
377    if (auth != NULL) {
378        AuthorizationBlob blob = *(AuthorizationBlob*)auth_token_get_key(auth);
379        dispatch_async(get_server_dispatch_queue(), ^{
380            LOGV("server: unregistering auth %p", auth);
381            CFDictionaryRemoveValue(gAuthTokenMap, &blob);
382        });
383    }
384}
385
386auth_token_t
387server_find_copy_auth_token(AuthorizationBlob * blob)
388{
389    __block auth_token_t auth = NULL;
390    if (blob != NULL) {
391        dispatch_sync(get_server_dispatch_queue(), ^{
392            auth = (auth_token_t)CFDictionaryGetValue(gAuthTokenMap, blob);
393            if (auth) {
394                CFRetain(auth);
395            }
396        });
397    }
398    return auth;
399}
400
401session_t
402server_find_copy_session(session_id_t sid, bool create)
403{
404    __block session_t session = NULL;
405
406    dispatch_sync(get_server_dispatch_queue(), ^{
407        session = (session_t)CFDictionaryGetValue(gSessionMap, &sid);
408        if (session) {
409            CFRetain(session);
410        } else if (create) {
411            session = session_create(sid);
412            if (session) {
413                CFDictionarySetValue(gSessionMap, session_get_key(session), session);
414            }
415        }
416    });
417
418    return session;
419}
420
421#pragma mark -
422#pragma mark API
423
424static OSStatus
425_process_find_copy_auth_token_from_xpc(process_t proc, xpc_object_t message, auth_token_t * auth_out)
426{
427    OSStatus status = errAuthorizationSuccess;
428    require_action(auth_out != NULL, done, status = errAuthorizationInternal);
429
430    size_t len;
431    AuthorizationBlob * blob = (AuthorizationBlob *)xpc_dictionary_get_data(message, AUTH_XPC_BLOB, &len);
432    require_action(blob != NULL, done, status = errAuthorizationInvalidRef);
433    require_action(len == sizeof(AuthorizationBlob), done, status = errAuthorizationInvalidRef);
434
435    auth_token_t auth = process_find_copy_auth_token(proc, blob);
436    require_action(auth != NULL, done, status = errAuthorizationInvalidRef);
437
438#if DEBUG
439    LOGV("server[%i]: authtoken lookup %#x%x %p", process_get_pid(proc), blob->data[1],blob->data[0], auth);
440#else
441    LOGV("server[%i]: authtoken lookup %p", process_get_pid(proc), auth);
442#endif
443
444    *auth_out = auth;
445
446done:
447    return status;
448}
449
450static OSStatus _server_authorize(connection_t conn, auth_token_t auth, AuthorizationFlags flags, auth_rights_t rights, auth_items_t enviroment, engine_t * engine_out)
451{
452    __block OSStatus status = errAuthorizationDenied;
453    engine_t engine = NULL;
454
455    require_action(conn, done, status = errAuthorizationInternal);
456
457    engine = engine_create(conn, auth);
458    require_action(engine, done, status = errAuthorizationInternal);
459
460    if (flags & kAuthorizationFlagInteractionAllowed) {
461        dispatch_sync(connection_get_dispatch_queue(conn), ^{
462            connection_set_engine(conn, engine);
463            status = engine_authorize(engine, rights, enviroment, flags);
464            connection_set_engine(conn, NULL);
465        });
466    } else {
467        status = engine_authorize(engine, rights, enviroment, flags);
468    }
469
470done:
471    if (engine) {
472        if (engine_out) {
473            *engine_out = engine;
474        } else {
475            CFRelease(engine);
476        }
477    }
478    return status;
479}
480
481// IN:  AUTH_XPC_RIGHTS, AUTH_XPC_ENVIROMENT, AUTH_XPC_FLAGS
482// OUT: AUTH_XPC_BLOB
483OSStatus
484authorization_create(connection_t conn, xpc_object_t message, xpc_object_t reply)
485{
486    OSStatus status = errAuthorizationDenied;
487
488    process_t proc = connection_get_process(conn);
489
490    // Passed in args
491    auth_rights_t rights = auth_rights_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_RIGHTS));
492    auth_items_t enviroment = auth_items_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_ENVIROMENT));
493    AuthorizationFlags flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS);
494
495    // Create Authorization Token
496    auth_token_t auth = auth_token_create(proc, flags & kAuthorizationFlagLeastPrivileged);
497    require_action(auth != NULL, done, status = errAuthorizationInternal);
498
499    if (!(flags & kAuthorizationFlagNoData)) {
500        process_add_auth_token(proc,auth);
501    }
502
503    status = _server_authorize(conn, auth, flags, rights, enviroment, NULL);
504    require_noerr(status, done);
505
506    //reply
507    xpc_dictionary_set_data(reply, AUTH_XPC_BLOB, auth_token_get_blob(auth), sizeof(AuthorizationBlob));
508
509done:
510    CFReleaseSafe(rights);
511    CFReleaseSafe(enviroment);
512    CFReleaseSafe(auth);
513    return status;
514}
515
516// IN:  AUTH_XPC_DATA, AUTH_XPC_ENVIROMENT, AUTH_XPC_FLAGS
517// OUT: AUTH_XPC_BLOB
518OSStatus authorization_create_with_audit_token(connection_t conn, xpc_object_t message, xpc_object_t reply)
519{
520    OSStatus status = errAuthorizationDenied;
521    auth_token_t auth = NULL;
522
523    process_t proc = connection_get_process(conn);
524    require(process_get_uid(proc) == 0, done);  //only root can use this call
525
526    // Passed in args
527    size_t len = 0;
528    const char * data = xpc_dictionary_get_data(message, AUTH_XPC_DATA, &len);
529    require(data != NULL, done);
530    require(len == sizeof(audit_token_t), done);
531
532//    auth_items_t enviroment = auth_items_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_ENVIROMENT));
533    AuthorizationFlags flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS);
534
535    audit_info_s auditInfo;
536    _server_parse_audit_token((audit_token_t*)data, &auditInfo);
537
538    // Create Authorization Token
539    auth = auth_token_create(proc, flags & kAuthorizationFlagLeastPrivileged);
540    require_action(auth != NULL, done, status = errAuthorizationInternal);
541
542    process_add_auth_token(proc,auth);
543
544    //reply
545    xpc_dictionary_set_data(reply, AUTH_XPC_BLOB, auth_token_get_blob(auth), sizeof(AuthorizationBlob));
546
547done:
548//    CFReleaseSafe(enviroment);
549    CFReleaseSafe(auth);
550    return status;
551}
552
553// IN:  AUTH_XPC_BLOB, AUTH_XPC_FLAGS
554// OUT:
555OSStatus
556authorization_free(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED)
557{
558    OSStatus status = errAuthorizationSuccess;
559    AuthorizationFlags flags = 0;
560    process_t proc = connection_get_process(conn);
561
562    auth_token_t auth = NULL;
563    status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
564    require_noerr(status, done);
565
566    flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS);
567
568    if (flags & kAuthorizationFlagDestroyRights) {
569        auth_token_credentials_iterate(auth, ^bool(credential_t cred) {
570            credential_invalidate(cred);
571            LOGV("engine[%i]: invalidating %scredential %s (%i) from authorization (%p)", connection_get_pid(conn), credential_get_shared(cred) ? "shared " : "", credential_get_name(cred), credential_get_uid(cred), auth);
572            return true;
573        });
574
575        session_credentials_purge(auth_token_get_session(auth));
576    }
577
578    process_remove_auth_token(proc, auth, flags);
579
580done:
581    CFReleaseSafe(auth);
582    LOGV("server[%i]: AuthorizationFree %i (flags:%x)", connection_get_pid(conn), status, flags);
583    return status;
584}
585
586// IN:  AUTH_XPC_BLOB, AUTH_XPC_RIGHTS, AUTH_XPC_ENVIROMENT, AUTH_XPC_FLAGS
587// OUT: AUTH_XPC_OUT_ITEMS
588OSStatus
589authorization_copy_rights(connection_t conn, xpc_object_t message, xpc_object_t reply)
590{
591    OSStatus status = errAuthorizationDenied;
592    engine_t engine = NULL;
593
594    process_t proc = connection_get_process(conn);
595
596    // Passed in args
597    auth_rights_t rights = auth_rights_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_RIGHTS));
598    auth_items_t enviroment = auth_items_create_with_xpc(xpc_dictionary_get_value(message, AUTH_XPC_ENVIROMENT));
599    AuthorizationFlags flags = (AuthorizationFlags)xpc_dictionary_get_uint64(message, AUTH_XPC_FLAGS);
600
601    auth_token_t auth = NULL;
602    status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
603    require_noerr(status, done);
604
605    status = _server_authorize(conn, auth, flags, rights, enviroment, &engine);
606    require_noerr(status, done);
607
608    //reply
609    xpc_object_t outItems = auth_rights_export_xpc(engine_get_granted_rights(engine));
610    xpc_dictionary_set_value(reply, AUTH_XPC_OUT_ITEMS, outItems);
611    xpc_release_safe(outItems);
612
613done:
614    CFReleaseSafe(rights);
615    CFReleaseSafe(enviroment);
616    CFReleaseSafe(auth);
617    CFReleaseSafe(engine);
618
619    return status;
620}
621
622// IN:  AUTH_XPC_BLOB, AUTH_XPC_TAG
623// OUT: AUTH_XPC_OUT_ITEMS
624OSStatus
625authorization_copy_info(connection_t conn, xpc_object_t message, xpc_object_t reply)
626{
627
628    OSStatus status = errAuthorizationSuccess;
629    auth_items_t items = NULL;
630    const char * tag = NULL;
631
632    process_t proc = connection_get_process(conn);
633
634    auth_token_t auth = NULL;
635    status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
636    require_noerr(status, done);
637
638    items = auth_items_create();
639
640    tag = xpc_dictionary_get_string(message, AUTH_XPC_TAG);
641    LOGV("server[%i]: requested tag: %s", connection_get_pid(conn), tag ? tag : "(all)");
642    if (tag) {
643        size_t len;
644        const void * data = auth_items_get_data(auth_token_get_context(auth), tag, &len);
645        if (data) {
646            auth_items_set_data(items, tag, data, len);
647        }
648    } else {
649        auth_items_copy(items, auth_token_get_context(auth));
650    }
651
652#if DEBUG
653    LOGV("server[%i]: Dumping requested AuthRef items", connection_get_pid(conn));
654    _show_cf(items);
655#endif
656
657    //reply
658    xpc_object_t outItems = auth_items_export_xpc(items);
659    xpc_dictionary_set_value(reply, AUTH_XPC_OUT_ITEMS, outItems);
660    xpc_release_safe(outItems);
661
662done:
663    CFReleaseSafe(items);
664    CFReleaseSafe(auth);
665    LOGV("server[%i]: AuthorizationCopyInfo %i", connection_get_pid(conn), status);
666    return status;
667}
668
669// IN:  AUTH_XPC_BLOB
670// OUT: AUTH_XPC_EXTERNAL
671OSStatus
672authorization_make_external_form(connection_t conn, xpc_object_t message, xpc_object_t reply)
673{
674    OSStatus status = errAuthorizationSuccess;
675
676    process_t proc = connection_get_process(conn);
677
678    auth_token_t auth = NULL;
679    status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
680    require_noerr(status, done);
681
682    AuthorizationExternalForm exForm;
683    AuthorizationExternalBlob * exBlob = (AuthorizationExternalBlob *)&exForm;
684    memset(&exForm, 0, sizeof(exForm));
685
686    exBlob->blob = *auth_token_get_blob(auth);
687    exBlob->session = process_get_session_id(proc);
688
689    xpc_dictionary_set_data(reply, AUTH_XPC_EXTERNAL, &exForm, sizeof(exForm));
690    server_register_auth_token(auth);
691
692done:
693    CFReleaseSafe(auth);
694    LOGV("server[%i]: AuthorizationMakeExternalForm %i", connection_get_pid(conn), status);
695    return status;
696}
697
698// IN:  AUTH_XPC_EXTERNAL
699// OUT: AUTH_XPC_BLOB
700OSStatus
701authorization_create_from_external_form(connection_t conn, xpc_object_t message, xpc_object_t reply)
702{
703    OSStatus status = errAuthorizationSuccess;
704    auth_token_t auth = NULL;
705
706    process_t proc = connection_get_process(conn);
707
708    size_t len;
709    AuthorizationExternalForm * exForm = (AuthorizationExternalForm *)xpc_dictionary_get_data(message, AUTH_XPC_EXTERNAL, &len);
710    require_action(exForm != NULL, done, status = errAuthorizationInternal);
711    require_action(len == sizeof(AuthorizationExternalForm), done, status = errAuthorizationInvalidRef);
712
713    AuthorizationExternalBlob * exBlob = (AuthorizationExternalBlob *)exForm;
714    auth = server_find_copy_auth_token(&exBlob->blob);
715    require_action(auth != NULL, done, status = errAuthorizationDenied);
716
717    process_add_auth_token(proc, auth);
718    xpc_dictionary_set_data(reply, AUTH_XPC_BLOB, auth_token_get_blob(auth), sizeof(AuthorizationBlob));
719
720done:
721    CFReleaseSafe(auth);
722    LOGV("server[%i]: AuthorizationCreateFromExternalForm %i", connection_get_pid(conn), status);
723    return status;
724}
725
726// IN:  AUTH_XPC_RIGHT_NAME
727// OUT: AUTH_XPC_DATA
728OSStatus
729authorization_right_get(connection_t conn AUTH_UNUSED, xpc_object_t message, xpc_object_t reply)
730{
731    OSStatus status = errAuthorizationDenied;
732    rule_t rule = NULL;
733    CFTypeRef cfdict = NULL;
734    xpc_object_t xpcdict = NULL;
735
736    authdb_connection_t dbconn = authdb_connection_acquire(server_get_database());
737    rule = rule_create_with_string(xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME), dbconn);
738    require(rule != NULL, done);
739    require(rule_get_id(rule) != 0, done);
740
741    cfdict = rule_copy_to_cfobject(rule, dbconn);
742    require(cfdict != NULL, done);
743
744    xpcdict = _CFXPCCreateXPCObjectFromCFObject(cfdict);
745    require(xpcdict != NULL, done);
746
747    // reply
748    xpc_dictionary_set_value(reply, AUTH_XPC_DATA, xpcdict);
749
750    status = errAuthorizationSuccess;
751
752done:
753    authdb_connection_release(&dbconn);
754    CFReleaseSafe(cfdict);
755    xpc_release_safe(xpcdict);
756    CFReleaseSafe(rule);
757    LOGV("server[%i]: AuthorizationRightGet %i", connection_get_pid(conn), status);
758    return status;
759}
760
761static bool _prompt_for_modifications(process_t proc, rule_t rule)
762{
763//    <rdar://problem/13853228> will put back it back at some later date
764//    SecRequirementRef ruleReq = rule_get_requirment(rule);
765//
766//    if (ruleReq && process_verify_requirment(proc, ruleReq)) {
767//        return false;
768//    }
769
770    return true;
771}
772
773static CFIndex _get_mechanism_index(CFArrayRef mechanisms, CFStringRef m_name)
774{
775    CFIndex index = -1;
776    require(mechanisms, done);
777
778    CFIndex c = CFArrayGetCount(mechanisms);
779    CFStringRef i_name = NULL;
780    for (CFIndex i = 0; i < c; ++i)
781    {
782        i_name = CFArrayGetValueAtIndex(mechanisms, i);
783        if (i_name && (CFGetTypeID(m_name) == CFStringGetTypeID())) {
784            if (CFStringCompare(i_name, m_name, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
785                index = i;
786                break;
787            }
788        }
789    }
790
791done:
792    return index;
793}
794
795static bool _update_rule_mechanism(authdb_connection_t dbconn, const char * rule_name, CFStringRef mechanism_name, CFStringRef insert_after_name, bool remove)
796{
797    bool updated = false;
798    rule_t rule = NULL;
799    rule_t update_rule = NULL;
800    CFMutableDictionaryRef cfdict = NULL;
801    CFStringRef update_name = NULL;
802
803    require(mechanism_name, done);
804
805    rule = rule_create_with_string(rule_name, dbconn);
806    require(rule_get_id(rule) != 0, done); // rule doesn't exist in the database
807
808    cfdict = rule_copy_to_cfobject(rule, dbconn);
809    require(cfdict != NULL, done);
810
811    CFMutableArrayRef mechanisms = NULL;
812    bool res = CFDictionaryGetValueIfPresent(cfdict, CFSTR(kAuthorizationRuleParameterMechanisms), (void*)&mechanisms);
813    require(res == true, done);
814
815    CFIndex index = -1;
816
817    if (remove) {
818        index = _get_mechanism_index(mechanisms, mechanism_name);
819    } else {
820        if (insert_after_name) {
821            if ((index = _get_mechanism_index(mechanisms, insert_after_name)) != -1) {
822                index++;
823            } else {
824                index = 0; // if we couldn't find the index add it to the begining
825            }
826        } else {
827            index = 0;
828        }
829    }
830
831    if (index != -1) {
832        if(remove) {
833            CFArrayRemoveValueAtIndex(mechanisms, index);
834        } else {
835            if (index < CFArrayGetCount(mechanisms)) {
836                require_action(CFStringCompare(CFArrayGetValueAtIndex(mechanisms, index), mechanism_name, kCFCompareCaseInsensitive) != kCFCompareEqualTo, done, updated = true);
837            }
838            CFArrayInsertValueAtIndex(mechanisms, index, mechanism_name);
839        }
840
841        CFDictionarySetValue(cfdict, CFSTR(kAuthorizationRuleParameterMechanisms), mechanisms);
842
843        // and write it back
844        update_name = CFStringCreateWithCString(kCFAllocatorDefault, rule_name, kCFStringEncodingUTF8);
845        require(update_name, done);
846        update_rule = rule_create_with_plist(rule_get_type(rule), update_name, cfdict, dbconn);
847        require(update_rule, done);
848
849        require(rule_sql_commit(update_rule, dbconn, CFAbsoluteTimeGetCurrent(), NULL), done);
850    }
851
852    updated = true;
853
854done:
855    CFReleaseSafe(rule);
856    CFReleaseSafe(update_rule);
857    CFReleaseSafe(cfdict);
858    CFReleaseSafe(update_name);
859    return updated;
860}
861
862/// IN:  AUTH_XPC_BLOB, AUTH_XPC_INT64
863// OUT:
864OSStatus
865authorization_enable_smartcard(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED)
866{
867    const CFStringRef SMARTCARD_LINE = CFSTR("builtin:smartcard-sniffer,privileged");
868    const CFStringRef BUILTIN_LINE = CFSTR("builtin:policy-banner");
869    const char* SYSTEM_LOGIN_CONSOLE = "system.login.console";
870    const char* AUTHENTICATE = "authenticate";
871
872    __block OSStatus status = errAuthorizationSuccess;
873    bool enable_smartcard = false;
874    authdb_connection_t dbconn = NULL;
875    auth_token_t auth = NULL;
876    auth_rights_t checkRight = NULL;
877
878    process_t proc = connection_get_process(conn);
879
880    status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
881    require_noerr(status, done);
882
883    checkRight = auth_rights_create();
884    auth_rights_add(checkRight, "config.modify.smartcard");
885    status = _server_authorize(conn, auth, kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights, checkRight, NULL, NULL);
886    require_noerr(status, done);
887
888    enable_smartcard = xpc_dictionary_get_bool(message, AUTH_XPC_DATA);
889
890    dbconn = authdb_connection_acquire(server_get_database());
891
892    if (!_update_rule_mechanism(dbconn, SYSTEM_LOGIN_CONSOLE, SMARTCARD_LINE, BUILTIN_LINE, enable_smartcard ? false : true)) {
893        status = errAuthorizationInternal;
894        LOGE("server[%i]: smartcard: enable(%i) failed to update %s", connection_get_pid(conn), enable_smartcard, SYSTEM_LOGIN_CONSOLE);
895    }
896    if (!_update_rule_mechanism(dbconn, AUTHENTICATE, SMARTCARD_LINE, NULL, enable_smartcard ? false : true)) {
897        status = errAuthorizationInternal;
898        LOGE("server[%i]: smartcard: enable(%i) failed to update %s", connection_get_pid(conn), enable_smartcard, AUTHENTICATE);
899    }
900
901    authdb_checkpoint(dbconn);
902
903done:
904    authdb_connection_release(&dbconn);
905    CFReleaseSafe(checkRight);
906    CFReleaseSafe(auth);
907    return status;
908}
909
910static int64_t _process_get_identifier_count(process_t proc, authdb_connection_t conn)
911{
912    __block int64_t result = 0;
913
914    authdb_step(conn, "SELECT COUNT(*) AS cnt FROM rules WHERE identifier = ? ", ^(sqlite3_stmt *stmt) {
915        sqlite3_bind_text(stmt, 1, process_get_identifier(proc), -1, NULL);
916    }, ^bool(auth_items_t data) {
917        result = auth_items_get_int64(data, "cnt");
918        return true;
919    });
920
921    return result;
922}
923
924static int64_t _get_max_process_rights()
925{
926    static dispatch_once_t onceToken;
927    static int64_t max_rights = MAX_PROCESS_RIGHTS;
928
929    //sudo defaults write /Library/Preferences/com.apple.authd max_process_rights -bool true
930    dispatch_once(&onceToken, ^{
931		CFTypeRef max = (CFNumberRef)CFPreferencesCopyValue(CFSTR("max_process_rights"), CFSTR(SECURITY_AUTH_NAME), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
932
933        if (max && CFGetTypeID(max) == CFNumberGetTypeID()) {
934            CFNumberGetValue(max, kCFNumberSInt64Type, &max_rights);
935        }
936        CFReleaseSafe(max);
937    });
938
939    return max_rights;
940}
941
942// IN:  AUTH_XPC_BLOB, AUTH_XPC_RIGHT_NAME, AUTH_XPC_DATA
943// OUT:
944OSStatus
945authorization_right_set(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED)
946{
947    __block OSStatus status = errAuthorizationDenied;
948    __block engine_t engine = NULL;
949    CFStringRef cf_rule_name = NULL;
950    CFDictionaryRef cf_rule_dict = NULL;
951    rule_t rule = NULL;
952    rule_t existingRule = NULL;
953    authdb_connection_t dbconn = NULL;
954    auth_token_t auth = NULL;
955    bool force_modify = false;
956    RuleType rule_type = RT_RIGHT;
957    const char * rule_name = NULL;
958    bool auth_rule = false;
959
960    process_t proc = connection_get_process(conn);
961
962    status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
963    require_noerr(status, done);
964
965    require_action(xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME) != NULL, done, status = errAuthorizationInternal);
966    require_action(xpc_dictionary_get_value(message, AUTH_XPC_DATA) != NULL, done, status = errAuthorizationInternal);
967
968    rule_name = xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME);
969    require(rule_name != NULL, done);
970
971    if (_compare_string(rule_name, "authenticate")) {
972        rule_type = RT_RULE;
973        auth_rule = true;
974    }
975
976    cf_rule_name = CFStringCreateWithCString(kCFAllocatorDefault, rule_name, kCFStringEncodingUTF8);
977    require(cf_rule_name != NULL, done);
978
979    cf_rule_dict = _CFXPCCreateCFObjectFromXPCObject(xpc_dictionary_get_value(message, AUTH_XPC_DATA));
980    require(cf_rule_dict != NULL, done);
981
982    dbconn = authdb_connection_acquire(server_get_database());
983
984    rule = rule_create_with_plist(rule_type, cf_rule_name, cf_rule_dict, dbconn);
985    if (process_get_uid(proc) != 0) {
986        require_action(rule_get_extract_password(rule) == false, done, status = errAuthorizationDenied; LOGE("server[%i]: AuthorizationRightSet not allowed to set extract-password. (denied)", connection_get_pid(conn)));
987    }
988
989    // if rule doesn't currently exist then we have to check to see if they are over the Max.
990    if (rule_get_id(rule) == 0) {
991        if (process_get_identifier(proc) == NULL) {
992            LOGE("server[%i]: AuthorizationRightSet required for process %s (missing code signature). To add rights to the Authorization database, your process must have a code signature.", connection_get_pid(conn), process_get_code_url(proc));
993            force_modify = true;
994        } else {
995            int64_t process_rule_count = _process_get_identifier_count(proc, dbconn);
996            if ((process_rule_count >= _get_max_process_rights())) {
997                if (!connection_get_syslog_warn(conn)) {
998                    LOGE("server[%i]: AuthorizationRightSet Denied API abuse process %s already contains %lli rights.", connection_get_pid(conn), process_get_code_url(proc), _get_max_process_rights());
999                    connection_set_syslog_warn(conn);
1000                }
1001                status = errAuthorizationDenied;
1002                goto done;
1003            }
1004        }
1005    } else {
1006        if (auth_rule) {
1007            if (process_get_uid(proc) != 0) {
1008                LOGE("server[%i]: AuthorizationRightSet denied, root required to update the 'authenticate' rule", connection_get_pid(conn));
1009                status = errAuthorizationDenied;
1010                goto done;
1011            }
1012        } else {
1013            // verify they are updating a right and not a rule
1014            existingRule = rule_create_with_string(rule_get_name(rule), dbconn);
1015            if (rule_get_type(existingRule) == RT_RULE) {
1016                LOGE("server[%i]: AuthorizationRightSet Denied updating '%s' rule is prohibited", connection_get_pid(conn), rule_get_name(existingRule));
1017                status = errAuthorizationDenied;
1018                goto done;
1019            }
1020        }
1021    }
1022
1023    if (_prompt_for_modifications(proc,rule)) {
1024        authdb_connection_release(&dbconn);
1025
1026        dispatch_sync(connection_get_dispatch_queue(conn), ^{
1027            engine = engine_create(conn, auth);
1028            connection_set_engine(conn, engine);
1029            status = engine_verify_modification(engine, rule, false, force_modify);
1030            connection_set_engine(conn, NULL);
1031        });
1032        require_noerr(status, done);
1033
1034        dbconn = authdb_connection_acquire(server_get_database());
1035    }
1036
1037    if (rule_sql_commit(rule, dbconn, engine ? engine_get_time(engine) : CFAbsoluteTimeGetCurrent(), proc)) {
1038        LOGV("server[%i]: Successfully updated rule %s", connection_get_pid(conn), rule_get_name(rule));
1039        authdb_checkpoint(dbconn);
1040        status = errAuthorizationSuccess;
1041    } else {
1042        LOGE("server[%i]: Failed to update rule %s", connection_get_pid(conn), rule_get_name(rule));
1043        status = errAuthorizationDenied;
1044    }
1045
1046done:
1047    authdb_connection_release(&dbconn);
1048    CFReleaseSafe(existingRule);
1049    CFReleaseSafe(cf_rule_name);
1050    CFReleaseSafe(cf_rule_dict);
1051    CFReleaseSafe(auth);
1052    CFReleaseSafe(rule);
1053    CFReleaseSafe(engine);
1054    return status;
1055}
1056
1057// IN:  AUTH_XPC_BLOB, AUTH_XPC_RIGHT_NAME
1058// OUT:
1059OSStatus
1060authorization_right_remove(connection_t conn, xpc_object_t message, xpc_object_t reply AUTH_UNUSED)
1061{
1062    __block OSStatus status = errAuthorizationDenied;
1063    __block engine_t engine = NULL;
1064    rule_t rule = NULL;
1065    authdb_connection_t dbconn = NULL;
1066
1067    process_t proc = connection_get_process(conn);
1068
1069    auth_token_t auth = NULL;
1070    status = _process_find_copy_auth_token_from_xpc(proc, message, &auth);
1071    require_noerr(status, done);
1072
1073    dbconn = authdb_connection_acquire(server_get_database());
1074
1075    rule = rule_create_with_string(xpc_dictionary_get_string(message, AUTH_XPC_RIGHT_NAME), dbconn);
1076    require(rule != NULL, done);
1077
1078    if (_prompt_for_modifications(proc,rule)) {
1079        authdb_connection_release(&dbconn);
1080
1081        dispatch_sync(connection_get_dispatch_queue(conn), ^{
1082            engine = engine_create(conn, auth);
1083            connection_set_engine(conn, engine);
1084            status = engine_verify_modification(engine, rule, true, false);
1085            connection_set_engine(conn, NULL);
1086        });
1087        require_noerr(status, done);
1088
1089        dbconn = authdb_connection_acquire(server_get_database());
1090    }
1091
1092    if (rule_get_id(rule) != 0) {
1093        rule_sql_remove(rule, dbconn);
1094    }
1095
1096done:
1097    authdb_connection_release(&dbconn);
1098    CFReleaseSafe(auth);
1099    CFReleaseSafe(rule);
1100    CFReleaseSafe(engine);
1101    LOGV("server[%i]: AuthorizationRightRemove %i", connection_get_pid(conn), status);
1102    return status;
1103}
1104
1105#pragma mark -
1106#pragma mark test code
1107
1108OSStatus
1109session_set_user_preferences(connection_t conn, xpc_object_t message, xpc_object_t reply)
1110{
1111    (void)conn;
1112    (void)message;
1113    (void)reply;
1114    return errAuthorizationSuccess;
1115}
1116
1117void
1118server_dev() {
1119//    rule_t rule = rule_create_with_string("system.preferences.accounts");
1120//    CFDictionaryRef dict = rule_copy_to_cfobject(rule);
1121//    _show_cf(dict);
1122//    CFReleaseSafe(rule);
1123//    CFReleaseSafe(dict);
1124
1125//    auth_items_t config = NULL;
1126//    double d2 = 0, d1 = 5;
1127//    authdb_get_key_value(server_get_authdb_reader(), "config", &config);
1128//    auth_items_set_double(config, "test", d1);
1129//    d2 = auth_items_get_double(config, "test");
1130//    LOGV("d1=%f d2=%f", d1, d2);
1131//    CFReleaseSafe(config);
1132
1133
1134//    auth_items_t items = auth_items_create();
1135//    auth_items_set_string(items, "test", "testing 1");
1136//    auth_items_set_string(items, "test2", "testing 2");
1137//    auth_items_set_string(items, "test3", "testing 3");
1138//    auth_items_set_flags(items, "test3", 4);
1139//    auth_items_set_string(items, "apple", "apple");
1140//    auth_items_set_flags(items, "apple", 1);
1141//    auth_items_set_int(items, "int", 45);
1142//    auth_items_set_flags(items, "int", 2);
1143//    auth_items_set_bool(items, "true", true);
1144//    auth_items_set_bool(items, "false", false);
1145//    auth_items_set(items, "com.apple.");
1146//    auth_show(items);
1147//    LOGD("Yeah it works: %s", auth_items_get_string(items, "test3"));
1148//    LOGD("Yeah it works: %i", auth_items_get_bool(items, "true"));
1149//    LOGD("Yeah it works: %i", auth_items_get_bool(items, "false"));
1150//    LOGD("Yeah it works: %i", auth_items_get_int(items, "int"));
1151//    (void)auth_items_get_bool(items, "test3");
1152//    AuthorizationItemSet * itemSet = auth_items_get_item_set(items);
1153//    for (uint32_t i = 0; i < itemSet->count; i++) {
1154//        LOGD("item: %s", itemSet->items[i].name);
1155//    }
1156//
1157//    xpc_object_t xpcdata = SerializeItemSet(auth_items_get_item_set(items));
1158//    auth_items_t items2 = auth_items_create_with_xpc(xpcdata);
1159//    xpc_release(xpcdata);
1160//    auth_items_remove_with_flags(items2, 7);
1161////    auth_items_set_string(items2, "test3", "testing 3 very good");
1162//    auth_items_copy_with_flags(items2, items, 7);
1163//    LOGD("Yeah it works: %s", auth_items_get_string(items2, "test3"));
1164//    auth_show(items2);
1165//    CFReleaseSafe(items2);
1166//
1167//    CFReleaseSafe(items);
1168}
1169
1170