1/* Copyright (c) 2012-2014 Apple Inc. All Rights Reserved. */
2
3#include "securityd_service.h"
4#include "securityd_service_client.h"
5#include <libaks.h>
6
7#include <sandbox.h>
8#include <vproc.h>
9#include <xpc/xpc.h>
10#include <xpc/private.h>
11#include <dispatch/dispatch.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <stdio.h>
15#include <errno.h>
16#include <assert.h>
17#include <syslog.h>
18#include <unistd.h>
19#include <pwd.h>
20#include <uuid/uuid.h>
21#include <bsm/libbsm.h>
22#include <copyfile.h>
23#include <AssertMacros.h>
24#include <Security/Security.h>
25#include <Security/SecKeychainPriv.h>
26
27#include <IOKit/IOKitLib.h>
28#include <Kernel/IOKit/crypto/AppleFDEKeyStoreDefs.h>
29
30#if DEBUG
31#define LOG(...)    syslog(LOG_ERR, ##__VA_ARGS__);
32#else
33#define LOG(...)
34#endif
35
36// exported from libaks.a
37kern_return_t aks_register_for_notifications(mach_port_t server_port, uintptr_t message_id);
38kern_return_t _aks_stash_create_internal(keybag_handle_t handle, bool stage_key, const void * passcode, int length);
39kern_return_t _aks_stash_load_internal(keybag_handle_t handle, bool verify, uint8_t * data, size_t length, keybag_handle_t * handle_out);
40kern_return_t _aks_stash_destroy_internal(void);
41kern_return_t _aks_stash_commit_internal(void ** data, int * length);
42
43const char * kb_home_path = "Library/Keychains";
44const char * kb_user_bag = "user.kb";
45const char * kb_stash_bag = "stash.kb";
46
47#define HEXBUF_LEN 2048
48
49typedef struct {
50    uid_t uid;
51    gid_t gid;
52    char * name;
53    char * home;
54} service_user_record_t;
55
56typedef enum {
57    kb_bag_type_user,
58    kb_bag_type_stash
59} kb_bag_type_t;
60
61static io_connect_t
62openiodev(void)
63{
64    io_registry_entry_t service;
65    io_connect_t conn;
66    kern_return_t kr;
67
68    service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kAppleFDEKeyStoreServiceName));
69    if (service == IO_OBJECT_NULL)
70        return IO_OBJECT_NULL;
71
72    kr = IOServiceOpen(service, mach_task_self(), 0, &conn);
73    if (kr != KERN_SUCCESS)
74        return IO_OBJECT_NULL;
75
76    kr = IOConnectCallMethod(conn, kAppleFDEKeyStoreUserClientOpen, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL);
77    if (kr != KERN_SUCCESS) {
78        IOServiceClose(conn);
79        return IO_OBJECT_NULL;
80    }
81
82    return conn;
83}
84
85static void
86closeiodev(io_connect_t conn)
87{
88    kern_return_t kr;
89    kr = IOConnectCallMethod(conn, kAppleFDEKeyStoreUserClientClose, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL);
90    if (kr != KERN_SUCCESS)
91        return;
92    IOServiceClose(conn);
93}
94
95static dispatch_queue_t
96_kb_service_get_dispatch_queue()
97{
98    static dispatch_once_t onceToken = 0;
99    static dispatch_queue_t connection_queue = NULL;
100
101    dispatch_once(&onceToken, ^{
102        connection_queue = dispatch_queue_create("kb-service-queue", DISPATCH_QUEUE_SERIAL);
103    });
104
105    return connection_queue;
106}
107
108static service_user_record_t * get_user_record(uid_t uid)
109{
110    service_user_record_t * ur = NULL;
111    long bufsize = 0;
112    if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
113        bufsize = 4096;
114    }
115    char buf[bufsize];
116    struct passwd pwbuf, *pw = NULL;
117    if ((getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0) && pw != NULL) {
118        ur = calloc(1u, sizeof(service_user_record_t));
119        require(ur, done);
120        ur->uid = pw->pw_uid;
121        ur->gid = pw->pw_gid;
122        ur->home = strdup(pw->pw_dir);
123        ur->name = strdup(pw->pw_name);
124    } else {
125        syslog(LOG_ERR, "failed to lookup user record for uid: %d", uid);
126    }
127
128done:
129    return ur;
130}
131
132static void free_user_record(service_user_record_t * ur)
133{
134    if (ur != NULL) {
135        if (ur->home) {
136            free(ur->home);
137        }
138        if (ur->name) {
139            free(ur->name);
140        }
141        free(ur);
142    }
143}
144
145static const char * get_host_uuid()
146{
147    static uuid_string_t hostuuid = {};
148    static dispatch_once_t onceToken;
149    dispatch_once(&onceToken, ^{
150        struct timespec timeout = {30, 0};
151        uuid_t uuid = {};
152        if (gethostuuid(uuid, &timeout) == 0) {
153            uuid_unparse(uuid, hostuuid);
154        } else {
155            syslog(LOG_ERR, "failed to get host uuid");
156        }
157    });
158
159    return hostuuid;
160}
161
162static char *
163_kb_copy_bag_filename(service_user_record_t * ur, kb_bag_type_t type)
164{
165    char * bag_file = NULL;
166    const char * name = NULL;
167
168    require(ur, done);
169    switch(type) {
170        case kb_bag_type_user:
171            name = kb_user_bag;
172            break;
173        case kb_bag_type_stash:
174            name = kb_stash_bag;
175            break;
176        default:
177            goto done;
178    }
179
180    bag_file = calloc(1u, PATH_MAX);
181    require(bag_file, done);
182
183    snprintf(bag_file, PATH_MAX, "%s/%s/%s/%s", ur->home, kb_home_path, get_host_uuid(), name);
184
185done:
186    return bag_file;
187}
188
189static bool
190_kb_verify_create_path(service_user_record_t * ur)
191{
192    bool created = false;
193    struct stat st_info = {};
194    char new_path[PATH_MAX] = {};
195    char kb_path[PATH_MAX] = {};
196    snprintf(kb_path, sizeof(kb_path), "%s/%s/%s", ur->home, kb_home_path, get_host_uuid());
197    if (lstat(kb_path, &st_info) == 0) {
198        if (S_ISDIR(st_info.st_mode)) {
199            created = true;
200        } else {
201            syslog(LOG_ERR, "invalid directory at '%s' moving aside", kb_path);
202            snprintf(new_path, sizeof(new_path), "%s-invalid", kb_path);
203            unlink(new_path);
204            if (rename(kb_path, new_path) != 0) {
205                syslog(LOG_ERR, "failed to rename file: %s (%s)", kb_path, strerror(errno));
206                goto done;
207            }
208        }
209    }
210    if (!created) {
211        require_action(mkpath_np(kb_path, 0700) == 0, done, syslog(LOG_ERR, "could not create path: %s (%s)", kb_path, strerror(errno)));
212        created = true;
213    }
214
215done:
216    return created;
217}
218
219static void
220_set_thread_credentials(service_user_record_t * ur)
221{
222    int rc = pthread_setugid_np(ur->uid, ur->gid);
223    if (rc) { syslog(LOG_ERR, "failed to set thread credential: %i (%s)", errno, strerror(errno)); }
224
225    rc = initgroups(ur->name, ur->gid);
226    if (rc) { syslog(LOG_ERR, "failed to initgroups: %i", rc); }
227}
228
229static void
230_clear_thread_credentials()
231{
232    int rc = pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE);
233    if (rc) { syslog(LOG_ERR, "failed to reset thread credential: %i (%s)", errno, strerror(errno)); }
234}
235
236static bool
237_kb_bag_exists(service_user_record_t * ur, const char * bag_file)
238{
239    bool exists = false;
240    struct stat st_info = {};
241    char new_file[PATH_MAX] = {};
242
243    require(ur, done);
244
245    _set_thread_credentials(ur);
246    if (lstat(bag_file, &st_info) == 0) {
247        if (S_ISREG(st_info.st_mode)) {
248            exists = true;
249        } else {
250            syslog(LOG_ERR, "invalid file at '%s' moving aside", bag_file);
251            snprintf(new_file, sizeof(new_file), "%s-invalid", bag_file);
252            unlink(new_file);
253            if (rename(bag_file, new_file) != 0) {
254                syslog(LOG_ERR, "failed to rename file: %s (%s)", bag_file, strerror(errno));
255            }
256        }
257    }
258
259done:
260    _clear_thread_credentials();
261    return exists;
262}
263
264static bool
265_kb_save_bag_to_disk(service_user_record_t * ur, const char * bag_file, void * data, size_t length)
266{
267    bool result = false;
268    int fd = -1;
269
270    require(bag_file, done);
271
272    _set_thread_credentials(ur);
273    require(_kb_verify_create_path(ur), done);
274
275    fd = open(bag_file, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0600);
276    require_action(fd != -1, done, syslog(LOG_ERR, "could not create file: %s (%s)", bag_file, strerror(errno)));
277    require_action(write(fd, data, length) != -1, done, syslog(LOG_ERR, "failed to write keybag to disk %s", strerror(errno)));
278
279    result = true;
280
281done:
282    if (fd != -1) { close(fd); }
283    _clear_thread_credentials();
284    return result;
285}
286
287static bool
288_kb_load_bag_from_disk(service_user_record_t * ur, const char * bag_file, uint8_t ** data, size_t * length)
289{
290    bool result = false;
291    int fd = -1;
292    uint8_t * buf = NULL;
293    size_t buf_size = 0;
294    struct stat st_info = {};
295
296    require(bag_file, done);
297
298    _set_thread_credentials(ur);
299    require(_kb_verify_create_path(ur), done);
300    require_quiet(lstat(bag_file, &st_info) == 0, done);
301    require_action(S_ISREG(st_info.st_mode), done, syslog(LOG_ERR, "failed to load, not a file: %s", bag_file));
302    buf_size = (size_t)st_info.st_size;
303
304    fd = open(bag_file, O_RDONLY | O_NOFOLLOW);
305    require_action(fd != -1, done, syslog(LOG_ERR, "could not open file: %s (%s)", bag_file, strerror(errno)));
306
307    buf = (uint8_t *)calloc(1u, buf_size);
308    require(buf != NULL, done);
309    require(read(fd, buf, buf_size) == buf_size, done);
310
311    *data = buf;
312    *length = buf_size;
313    buf = NULL;
314    result = true;
315
316done:
317    if (fd != -1) { close(fd); }
318    if (buf) { free(buf); }
319    _clear_thread_credentials();
320    return result;
321}
322
323static void
324_kb_rename_bag_on_disk(service_user_record_t * ur, const char * bag_file)
325{
326    char new_file[PATH_MAX] = {};
327    if (bag_file) {
328        _set_thread_credentials(ur);
329        snprintf(new_file, sizeof(new_file), "%s-invalid", bag_file);
330        unlink(new_file);
331        rename(bag_file, new_file);
332        _clear_thread_credentials();
333    }
334}
335
336static void
337_kb_delete_bag_on_disk(service_user_record_t * ur, const char * bag_file)
338{
339    if (bag_file) {
340        _set_thread_credentials(ur);
341        unlink(bag_file);
342        _clear_thread_credentials();
343    }
344}
345
346static int
347_kb_get_session_handle(service_context_t * context, keybag_handle_t * handle_out)
348{
349    int rc = KB_BagNotLoaded;
350    keybag_handle_t session_handle = bad_keybag_handle;
351    require_noerr_quiet(aks_get_system(context->s_uid, &session_handle), done);
352
353    *handle_out = session_handle;
354    rc = KB_Success;
355
356done:
357    return rc;
358}
359
360static void update_keybag_handle(keybag_handle_t handle)
361{
362    dispatch_sync(_kb_service_get_dispatch_queue(), ^{
363        uid_t uid = abs(handle);
364        uint8_t * buf = NULL;
365        size_t buf_size = 0;
366        service_user_record_t * ur = NULL;
367        char * bag_file = NULL;
368
369        require_noerr(aks_save_bag(handle, (void**)&buf, (int*)&buf_size), done);
370        require(ur = get_user_record(uid), done);
371        require(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user), done);
372        require(_kb_save_bag_to_disk(ur, bag_file, buf, buf_size), done);
373
374        syslog(LOG_NOTICE, "successfully updated handle %d", handle);
375
376    done:
377        if (buf) free(buf);
378        if (ur) free_user_record(ur);
379        if (bag_file) free(bag_file);
380    });
381}
382
383static int
384service_kb_create(service_context_t * context, const void * secret, int secret_len)
385{
386    __block int rc = KB_GeneralError;
387
388    dispatch_sync(_kb_service_get_dispatch_queue(), ^{
389        uint8_t * buf = NULL;
390        size_t buf_size = 0;
391        keybag_handle_t session_handle = bad_keybag_handle;
392        service_user_record_t * ur = get_user_record(context->s_uid);
393        char * bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user);
394
395        require(bag_file, done);
396
397        // check for the existance of the bagfile
398        require_action(!_kb_bag_exists(ur, bag_file), done, rc = KB_BagExists);
399
400        require_noerr(rc = aks_create_bag(secret, secret_len, kAppleKeyStoreDeviceBag, &session_handle), done);
401        require_noerr(rc = aks_save_bag(session_handle, (void**)&buf, (int*)&buf_size), done);
402        require_action(_kb_save_bag_to_disk(ur, bag_file, buf, buf_size), done, rc = KB_BagError);
403        require_noerr(rc = aks_set_system(session_handle, context->s_uid), done);
404        aks_unload_bag(session_handle);
405        require_noerr(rc = _kb_get_session_handle(context, &session_handle), done);
406
407        if (secret && rc == KB_Success) {
408            aks_unlock_bag(session_handle, secret, secret_len);
409        }
410
411    done:
412        if (buf) free(buf);
413        if (bag_file) { free(bag_file); }
414        if (ur) free_user_record(ur);
415    });
416
417    return rc;
418}
419
420static int
421service_kb_load(service_context_t * context)
422{
423    __block int rc = KB_GeneralError;
424
425    dispatch_sync(_kb_service_get_dispatch_queue(), ^{
426        uint8_t * buf = NULL;
427        size_t buf_size = 0;
428        keybag_handle_t session_handle = bad_keybag_handle;
429        service_user_record_t * ur = NULL;
430        char * bag_file = NULL;
431
432        rc = aks_get_system(context->s_uid, &session_handle);
433        if (rc == kIOReturnNotFound) {
434            require_action(ur = get_user_record(context->s_uid), done, rc = KB_GeneralError);
435            require_action(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user), done, rc = KB_GeneralError);
436            require_action_quiet(_kb_load_bag_from_disk(ur, bag_file, &buf, &buf_size), done, rc = KB_BagNotFound);
437            rc = aks_load_bag(buf, (int)buf_size, &session_handle);
438            if (rc == kIOReturnNotPermitted) {
439                syslog(LOG_ERR, "error loading keybag for uid (%i) in session (%i)", context->s_uid, context->s_id);
440                _kb_rename_bag_on_disk(ur, bag_file);
441                rc = KB_BagNotFound;
442            }
443            require_noerr(rc, done);
444            require_noerr(rc = aks_set_system(session_handle, context->s_uid), done);
445            aks_unload_bag(session_handle);
446        }
447        require(rc == KB_Success, done);
448
449    done:
450        if (buf) free(buf);
451        if (ur) free_user_record(ur);
452        if (bag_file) free(bag_file);
453    });
454
455    return rc;
456}
457
458
459static int
460service_kb_unlock(service_context_t * context, const void * secret, int secret_len)
461{
462    int rc = KB_GeneralError;
463    keybag_handle_t session_handle;
464    require_noerr(rc = _kb_get_session_handle(context, &session_handle), done);
465
466    rc = aks_unlock_bag(session_handle, secret, secret_len);
467
468done:
469    return rc;
470}
471
472static int
473service_kb_lock(service_context_t * context)
474{
475    int rc = KB_GeneralError;
476    keybag_handle_t session_handle;
477    require_noerr(rc = _kb_get_session_handle(context, &session_handle), done);
478
479    rc = aks_lock_bag(session_handle);
480
481done:
482    return rc;
483}
484
485static int
486service_kb_change_secret(service_context_t * context, const void * secret, int secret_len, const void * new_secret, int new_secret_len)
487{
488    __block int rc = KB_GeneralError;
489    keybag_handle_t session_handle;
490    require_noerr(rc = _kb_get_session_handle(context, &session_handle), done);
491
492    dispatch_sync(_kb_service_get_dispatch_queue(), ^{
493        uint8_t * buf = NULL;
494        size_t buf_size = 0;
495        service_user_record_t * ur = NULL;
496        char * bag_file = NULL;
497
498        require_noerr(rc = aks_change_secret(session_handle, secret, secret_len, new_secret, new_secret_len, NULL, NULL), done);
499        require_noerr(rc = aks_save_bag(session_handle, (void**)&buf, (int*)&buf_size), done);
500        require_action(ur = get_user_record(context->s_uid), done, rc = KB_GeneralError);
501        require_action(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user), done, rc = KB_GeneralError);
502        require_action(_kb_save_bag_to_disk(ur, bag_file, buf, buf_size), done, rc = KB_BagError);
503
504        rc = KB_Success;
505
506    done:
507        if (buf) free(buf);
508        if (ur) free_user_record(ur);
509        if (bag_file) free(bag_file);
510        return;
511    });
512
513done:
514    return rc;
515}
516
517static int
518service_kb_reset(service_context_t * context, const void * secret, int secret_len)
519{
520    __block int rc = KB_GeneralError;
521    service_user_record_t * ur = NULL;
522    char * bag_file = NULL;
523
524    require_action(ur = get_user_record(context->s_uid), done, rc = KB_GeneralError);
525    require_action(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user), done, rc = KB_GeneralError);
526
527    dispatch_sync(_kb_service_get_dispatch_queue(), ^{
528        uint8_t * buf = NULL;
529        size_t buf_size = 0;
530        keybag_handle_t session_handle = bad_keybag_handle;
531
532        syslog(LOG_ERR, "resetting keybag for uid (%i) in session (%i)", context->s_uid, context->s_id);
533        _kb_rename_bag_on_disk(ur, bag_file);
534
535        require_noerr(rc = aks_create_bag(secret, secret_len, kAppleKeyStoreDeviceBag, &session_handle), done);
536        require_noerr(rc = aks_save_bag(session_handle, (void**)&buf, (int*)&buf_size), done);
537        require_action(_kb_save_bag_to_disk(ur, bag_file, buf, buf_size), done, rc = KB_BagError);
538        require_noerr(rc = aks_set_system(session_handle, context->s_uid), done);
539        aks_unload_bag(session_handle);
540        require_noerr(rc = _kb_get_session_handle(context, &session_handle), done);
541
542        if (secret && rc == KB_Success) {
543            aks_unlock_bag(session_handle, secret, secret_len);
544        }
545
546    done:
547        if (buf) free(buf);
548        return;
549    });
550
551done:
552    if (ur) free_user_record(ur);
553    if (bag_file) free(bag_file);
554    return rc;
555}
556
557static int
558service_kb_is_locked(service_context_t * context, xpc_object_t reply)
559{
560    int rc = KB_GeneralError;
561    keybag_state_t state;
562    keybag_handle_t session_handle;
563    require_noerr(rc = _kb_get_session_handle(context, &session_handle), done);
564
565    require_noerr(rc = aks_get_lock_state(session_handle, &state), done);
566
567    xpc_dictionary_set_bool(reply, SERVICE_XPC_LOCKED, state & keybag_state_locked);
568    xpc_dictionary_set_bool(reply, SERVICE_XPC_NO_PIN, state & keybag_state_no_pin);
569
570done:
571    return rc;
572}
573
574static int
575service_kb_stash_create(service_context_t * context, const void * key, unsigned key_size)
576{
577    int rc = KB_GeneralError;
578    char * bag_file = NULL;
579    keybag_handle_t session_handle;
580    service_user_record_t * ur = NULL;
581    void * stashbag = NULL;
582    unsigned stashbag_size = 0;
583    __block bool saved = false;
584
585    require(key, done);
586    require_noerr(rc = _kb_get_session_handle(context, &session_handle), done);
587    require_action(ur = get_user_record(context->s_uid), done, rc = KB_GeneralError);
588    require_noerr(rc = _aks_stash_create_internal(session_handle, false, key, key_size), done);
589    require_noerr(rc = _aks_stash_commit_internal((void**)&stashbag, (int*)&stashbag_size), done);
590
591    require_action(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_stash), done, rc = KB_GeneralError);
592
593    // sync writing the bag to disk
594    dispatch_sync(_kb_service_get_dispatch_queue(), ^{
595        saved = _kb_save_bag_to_disk(ur, bag_file, stashbag, stashbag_size);
596    });
597    require_action(saved, done, rc = KB_BagError);
598    rc = KB_Success;
599
600done:
601    if (stashbag) { free(stashbag); }
602    if (bag_file) { free(bag_file); }
603    if (ur) free_user_record(ur);
604    return rc;
605}
606
607static int
608service_kb_stash_load(service_context_t * context, const void * key, unsigned key_size, bool nondestructive)
609{
610    __block int rc = KB_GeneralError;
611    char * bag_file = NULL;
612    keybag_handle_t session_handle;
613    service_user_record_t * ur = NULL;
614    __block uint8_t * stashbag = NULL;
615    __block size_t stashbag_size = 0;
616
617    require(key, done);
618    require_noerr(rc = _kb_get_session_handle(context, &session_handle), done);
619    require_action(ur = get_user_record(context->s_uid), done, rc = KB_GeneralError);
620    require_action(bag_file = _kb_copy_bag_filename(ur, kb_bag_type_stash), done, rc = KB_GeneralError);
621
622    // sync loading the bag from disk
623    dispatch_sync(_kb_service_get_dispatch_queue(), ^{
624        if (!_kb_load_bag_from_disk(ur, bag_file, &stashbag, &stashbag_size)) {
625            rc = KB_BagError;
626        }
627    });
628    require_noerr(rc, done);
629
630    require_noerr(rc = _aks_stash_create_internal(session_handle, true, key, key_size), done);
631    require_noerr(rc = _aks_stash_load_internal(session_handle, false, stashbag, stashbag_size, NULL), done);
632    rc = KB_Success;
633
634done:
635    if (stashbag) { free(stashbag); }
636    if ((bag_file) && (!nondestructive)) {
637        _kb_delete_bag_on_disk(ur, bag_file);
638        free(bag_file);
639    }
640    if (ur) free_user_record(ur);
641    return rc;
642}
643
644//
645// Get the keychain master key from the AppleFDEKeyStore.
646// Note that this is a one-time call - the master key is
647// removed from the keystore after it is returned.
648// Requires the entitlement: com.apple.private.securityd.keychain
649//
650OSStatus service_stash_get_key(service_context_t * context, xpc_object_t event, xpc_object_t reply)
651{
652    getStashKey_InStruct_t inStruct;
653    getStashKey_OutStruct_t outStruct;
654    size_t outSize = sizeof(outStruct);
655    kern_return_t kr = KERN_INVALID_ARGUMENT;
656
657    io_connect_t conn = openiodev();
658    require(conn, done);
659    inStruct.type = kAppleFDEKeyStoreStash_master;
660
661    kr = IOConnectCallMethod(conn, kAppleFDEKeyStore_getStashKey,
662                             NULL, 0,
663                             &inStruct, sizeof(inStruct),
664                             NULL, NULL,
665                             &outStruct, &outSize);
666
667    if (kr == KERN_SUCCESS) {
668        xpc_dictionary_set_data(reply, SERVICE_XPC_KEY, outStruct.outBuf.key.key, outStruct.outBuf.key.keysize);
669        service_kb_stash_load(context, outStruct.outBuf.key.key, outStruct.outBuf.key.keysize, false);
670    }
671
672done:
673    if (conn)
674        closeiodev(conn);
675
676    return kr;
677}
678
679//
680// Stash the keychain master key in the AppleFDEKeyStore and
681// flag it as the keychain master key to be added to the
682// reboot NVRAM blob.
683// This requires two calls to the AKS: the first to store the
684// key and get its uuid.  The second uses the uuid to flag the
685// key for blob inclusion.
686//
687OSStatus service_stash_set_key(service_context_t * context, xpc_object_t event, xpc_object_t reply)
688{
689    kern_return_t kr = KERN_INVALID_ARGUMENT;
690    size_t keydata_len = 0;
691    size_t len;
692
693    io_connect_t conn = openiodev();
694    require(conn, done);
695
696    // Store the key in the keystore and get its uuid
697    setKeyGetUUID_InStruct_t inStruct1;
698    uuid_OutStruct_t outStruct1;
699
700
701    const uint8_t *keydata = xpc_dictionary_get_data(event, SERVICE_XPC_KEY, &keydata_len);
702    require(keydata, done);
703
704    memcpy(&inStruct1.inKey.key.key, keydata, keydata_len);
705    inStruct1.inKey.key.keysize = (cryptosize_t) keydata_len;
706    len = sizeof(outStruct1);
707    kr = IOConnectCallMethod(conn, kAppleFDEKeyStore_setKeyGetUUID,
708                             NULL, 0,
709                             &inStruct1, sizeof(inStruct1),
710                             NULL, NULL,
711                             &outStruct1, &len);
712    require(kr == KERN_SUCCESS, done);
713
714    // Now using the uuid stash it as the master key
715    setStashKey_InStruct_t inStruct2;
716    memcpy(&inStruct2.uuid, &outStruct1.uuid, sizeof(outStruct1.uuid));
717    inStruct2.type  = kAppleFDEKeyStoreStash_master;
718
719    kr = IOConnectCallMethod(conn, kAppleFDEKeyStore_setStashKey,
720                             NULL, 0,
721                             &inStruct2, sizeof(inStruct2),
722                             NULL, NULL,
723                             NULL, NULL);
724
725    if  (kr == KERN_SUCCESS) {
726        service_kb_stash_create(context, keydata, (unsigned)keydata_len);
727    }
728done:
729    if (conn)
730        closeiodev(conn);
731
732    return kr;
733}
734
735//
736// Load the master stash key
737//
738OSStatus service_stash_load_key(service_context_t * context, xpc_object_t event, xpc_object_t reply)
739{
740    kern_return_t kr = KERN_SUCCESS;
741    size_t keydata_len = 0;
742
743    const uint8_t *keydata = xpc_dictionary_get_data(event, SERVICE_XPC_KEY, &keydata_len);
744    require(keydata, done);
745
746    kr = service_kb_stash_load(context, keydata, (cryptosize_t) keydata_len, true);
747done:
748
749    return kr;
750}
751
752//
753// Signal the AppleFDEKeyStore to take the tagged FDE key
754// and keychain master key, stash them in an encrypted
755// blob structure and write the blob to NVRAM.  The random
756// encryption key is written to the SMC.
757//
758#if DEBUG
759OSStatus service_stash_blob(xpc_object_t event, xpc_object_t reply)
760{
761    kern_return_t kr = KERN_INVALID_ARGUMENT;
762
763    io_connect_t conn = openiodev();
764    require(conn, done);
765
766    kr = IOConnectCallMethod(conn, kAppleFDEKeyStore_commitStash,
767                             NULL, 0,
768                             NULL, 0,
769                             NULL, NULL,
770                             NULL, NULL);
771done:
772    if (conn)
773        closeiodev(conn);
774
775    return kr;
776}
777#endif
778
779bool peer_has_entitlement(xpc_connection_t peer, const char * entitlement)
780{
781    bool entitled = false;
782
783    xpc_object_t value = xpc_connection_copy_entitlement_value(peer, entitlement);
784    if (value && (xpc_get_type(value) == XPC_TYPE_BOOL)) {
785        entitled = xpc_bool_get_value(value);
786    }
787
788    if (value) xpc_release(value);
789    return entitled;
790}
791
792#if DEBUG
793static char *
794to_hex(char * dst, const void * src, size_t size)
795{
796    int notleading = 0;
797
798    if ((size * 2) > HEXBUF_LEN) return NULL;
799
800    uint8_t * buf = (uint8_t *)src;
801    register char *chp = dst;
802    *dst = '\0';
803    if (size != 0) do {
804        if(notleading || *buf != '\0') {
805            if(!notleading && (*buf & 0xf0) == 0) {
806                sprintf(chp, "%.1x", * (unsigned char *) src);
807                chp += 1;
808            }
809            else {
810                sprintf(chp, "%.2x", * (unsigned char *) src);
811                chp += 2;
812            }
813            notleading = 1;
814        }
815        ++src;
816    } while (--size != 0);
817    return dst;
818}
819#endif // DEBUG
820
821static char * sel_to_char(uint64_t sel)
822{
823    switch (sel) {
824        case SERVICE_STASH_SET_KEY:
825            return "set_key";
826        case SERVICE_STASH_GET_KEY:
827            return "get_key";
828        case SERVICE_STASH_BLOB:
829            return "stash_blob";
830        case SERVICE_KB_LOAD:
831            return "kb_load";
832        case SERVICE_KB_UNLOCK:
833            return "kb_unlock";
834        case SERVICE_KB_LOCK:
835            return "kb_lock";
836        case SERVICE_KB_CHANGE_SECRET:
837            return "kb_change_secret";
838        case SERVICE_KB_CREATE:
839            return "kb_create";
840        case SERVICE_KB_IS_LOCKED:
841            return "kb_is_locked";
842        case SERVICE_KB_RESET:
843            return "kb_reset";
844        default:
845            return "unknown";
846    }
847}
848
849static char * err_to_char(int err)
850{
851    switch (err) {
852        case KB_Success:
853            return "success";
854        case KB_GeneralError:
855            return "general error";
856        case KB_BagNotFound:
857            return "bag not found";
858        case KB_BagError:
859            return "bag error";
860        case KB_BagNotLoaded:
861            return "bag not loaded";
862        case KB_BagExists:
863            return "bag exists";
864        case KB_InvalidSession:
865            return "invalid session";
866        default:
867            return "";
868    }
869}
870
871void service_peer_event_handler(xpc_connection_t connection, xpc_object_t event)
872{
873#if DEBUG
874    char hexbuf1[HEXBUF_LEN];
875    char hexbuf2[HEXBUF_LEN];
876#endif // DEBUG
877    xpc_type_t type = xpc_get_type(event);
878
879    if (type == XPC_TYPE_ERROR) {
880		if (event == XPC_ERROR_CONNECTION_INVALID) {
881        }
882    } else {
883        assert(type == XPC_TYPE_DICTIONARY);
884
885        int rc = KB_GeneralError;
886        uint64_t request = 0;
887        const uint8_t * secret = NULL, * new_secret = NULL;
888        size_t secret_len = 0, new_secret_len = 0, data_len = 0;
889        service_context_t * context = NULL;
890        const void * data;
891
892        xpc_object_t reply = xpc_dictionary_create_reply(event);
893
894        data = xpc_dictionary_get_data(event, SERVICE_XPC_CONTEXT, &data_len);
895        require(data, done);
896        require(data_len == sizeof(service_context_t), done);
897        context = (service_context_t*)data;
898
899        request = xpc_dictionary_get_uint64(event, SERVICE_XPC_REQUEST);
900
901        require_action(context->s_id != AU_DEFAUDITSID, done, rc = KB_InvalidSession);
902        require_action(context->s_uid != AU_DEFAUDITID, done, rc = KB_InvalidSession); // we only want to work in actual user sessions.
903
904        switch (request) {
905            case SERVICE_KB_CREATE:
906                //                if (kb_service_has_entitlement(peer, "com.apple.keystore.device")) {
907                secret = xpc_dictionary_get_data(event, SERVICE_XPC_SECRET, &secret_len);
908                rc = service_kb_create(context, secret, (int)secret_len);
909                //                }
910                break;
911            case SERVICE_KB_LOAD:
912                rc = service_kb_load(context);
913                break;
914            case SERVICE_KB_UNLOCK:
915                secret = xpc_dictionary_get_data(event, SERVICE_XPC_SECRET, &secret_len);
916                rc = service_kb_unlock(context, secret, (int)secret_len);
917                break;
918            case SERVICE_KB_LOCK:
919                rc = service_kb_lock(context);
920                break;
921            case SERVICE_KB_CHANGE_SECRET:
922                secret = xpc_dictionary_get_data(event, SERVICE_XPC_SECRET, &secret_len);
923                new_secret = xpc_dictionary_get_data(event, SERVICE_XPC_SECRET_NEW, &new_secret_len);
924                rc = service_kb_change_secret(context, secret, (int)secret_len, new_secret, (int)new_secret_len);
925                break;
926            case SERVICE_KB_RESET:
927                secret = xpc_dictionary_get_data(event, SERVICE_XPC_SECRET, &secret_len);
928                rc = service_kb_reset(context, secret, (int)secret_len);
929                break;
930            case SERVICE_KB_IS_LOCKED:
931                rc = service_kb_is_locked(context, reply);
932                break;
933            case SERVICE_STASH_GET_KEY:
934                rc = service_stash_get_key(context, event, reply);
935                break;
936            case SERVICE_STASH_SET_KEY:
937                rc = service_stash_set_key(context, event, reply);
938                break;
939            case SERVICE_STASH_LOAD_KEY:
940                rc = service_stash_load_key(context, event, reply);
941                break;
942#if DEBUG
943            case SERVICE_STASH_BLOB:
944                rc = service_stash_blob(event, reply);
945                break;
946#endif
947            default:
948                LOG("unknown service type");
949                break;
950        }
951
952    done:
953#if DEBUG
954        LOG("selector: %s (%llu), error: %s (%x), '%s' secret_len: %zu, '%s' new_secret_len: %zu, sid: %d, suid: %d", sel_to_char(request), request, err_to_char(rc), rc, to_hex(hexbuf1, secret, secret_len), secret_len, to_hex(hexbuf2, new_secret, new_secret_len), new_secret_len, context ? context->s_id : 0, context ? context->s_uid : 0);
955#else
956        if (rc != 0) {
957            syslog(LOG_NOTICE, "selector: %s (%llu), error: %s (%x), sid: %d, suid: %d", sel_to_char(request), request, err_to_char(rc), rc, context ? context->s_id : 0, context ? context->s_uid : 0);
958        }
959#endif
960        xpc_dictionary_set_int64(reply, SERVICE_XPC_RC, rc);
961        xpc_connection_send_message(connection, reply);
962        xpc_release(reply);
963    }
964}
965
966bool check_signature(xpc_connection_t connection)
967{
968    CFStringRef reqStr = CFSTR("identifier com.apple.securityd and anchor apple");
969    SecRequirementRef  requirement = NULL;
970    SecCodeRef codeRef = NULL;
971    CFMutableDictionaryRef codeDict = NULL;
972    CFNumberRef codePid = NULL;
973    pid_t pid = xpc_connection_get_pid(connection);
974
975    OSStatus status = SecRequirementCreateWithString(reqStr, kSecCSDefaultFlags, &requirement);
976    require_action(status == errSecSuccess, done, LOG("failed to create requirement"));
977
978    codeDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
979    codePid = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid);
980    CFDictionarySetValue(codeDict, kSecGuestAttributePid, codePid);
981    status = SecCodeCopyGuestWithAttributes(NULL, codeDict, kSecCSDefaultFlags, &codeRef);
982    require_action(status == errSecSuccess, done, LOG("failed to get code ref"));
983
984    status = SecCodeCheckValidity(codeRef, kSecCSDefaultFlags,
985#if DEBUG || RC_BUILDIT_YES
986                                  NULL);
987#else
988                                  requirement);
989#endif
990    require_action(status == errSecSuccess, done, syslog(LOG_ERR, "pid %d, does not satisfy code requirment (%d)", pid, status));
991
992done:
993    if (codeRef) CFRelease(codeRef);
994    if (requirement) CFRelease(requirement);
995    if (codeDict) CFRelease(codeDict);
996    if (codePid) CFRelease(codePid);
997
998    return (status == errSecSuccess);
999}
1000
1001static void register_for_notifications()
1002{
1003    __block kern_return_t kr;
1004    static mach_port_t mp = MACH_PORT_NULL;
1005
1006    static dispatch_once_t onceToken = 0;
1007    dispatch_once(&onceToken, ^{
1008        kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
1009        if (kr == KERN_SUCCESS) {
1010            dispatch_source_t mach_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
1011            dispatch_source_set_event_handler(mach_src, ^{
1012                mach_msg_return_t mr;
1013                uint8_t buf[sizeof(aks_notification_msg_t) + MAX_TRAILER_SIZE] = {};
1014                aks_notification_msg_t * msg = (aks_notification_msg_t*)buf;
1015                mr = mach_msg((mach_msg_header_t*)&buf, MACH_RCV_MSG, 0, sizeof(buf), mp, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
1016                if (mr == MACH_MSG_SUCCESS && msg->hdr.msgh_id == AKS_NOTIFICATION_MSGID) {
1017                    // ignored for now
1018                } else if (mr == MACH_MSG_SUCCESS && msg->hdr.msgh_id == AKS_NOTIFICATION_WRITE_SYSTEM_KEYBAG) {
1019                    syslog(LOG_NOTICE, "request to update handle %d", msg->handle);
1020                    update_keybag_handle(msg->handle);
1021                } else {
1022                    syslog(LOG_ERR, "mach_msg error: %x", mr);
1023                }
1024            });
1025            dispatch_resume(mach_src);
1026        } else {
1027            syslog(LOG_NOTICE, "failed to create notification port");
1028        }
1029
1030    });
1031
1032    kr = aks_register_for_notifications(mp, AKS_NOTIFICATION_WRITE_SYSTEM_KEYBAG);
1033    if (kr == KERN_SUCCESS) {
1034        syslog(LOG_NOTICE, "registered for notifications");
1035    } else {
1036        syslog(LOG_NOTICE, "failed to register for notifications %d", kr);
1037    }
1038}
1039
1040int main(int argc, const char * argv[])
1041{
1042    char * errorbuf;
1043    if (sandbox_init(SECURITYD_SERVICE_NAME, SANDBOX_NAMED, &errorbuf) != 0) {
1044        syslog(LOG_ERR, "sandbox_init failed %s", errorbuf);
1045        sandbox_free_error(errorbuf);
1046#ifndef DEBUG
1047        abort();
1048#endif
1049    }
1050
1051    register_for_notifications();
1052
1053    xpc_connection_t listener = xpc_connection_create_mach_service(SECURITYD_SERVICE_NAME, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
1054    xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {
1055        // It is safe to cast 'peer' to xpc_connection_t assuming
1056        // we have a correct configuration in our launchd.plist.
1057
1058        if (xpc_connection_get_euid(peer) != 0) {
1059            xpc_connection_cancel(peer);
1060            return;
1061        }
1062
1063        if (!check_signature(peer)) {
1064            xpc_connection_cancel(peer);
1065            return;
1066        }
1067
1068        xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {
1069            vproc_transaction_t transaction = vproc_transaction_begin(NULL);
1070            service_peer_event_handler(peer, event);
1071            vproc_transaction_end(NULL, transaction);
1072        });
1073        xpc_connection_resume(peer);
1074    });
1075    xpc_connection_resume(listener);
1076
1077    dispatch_main();
1078    exit(EXIT_FAILURE);
1079}
1080
1081