From 2177594423af9deaf05179eac7f2a0aa33eb6006 Mon Sep 17 00:00:00 2001 From: Apple Date: Wed, 13 Nov 2013 21:06:20 +0000 Subject: [PATCH] securityd-55199.0.1.tar.gz --- securityd_service/securityd_service/main.c | 123 ++++++++++++++++----- src/dbcrypto.cpp | 9 ++ src/dbcrypto.h | 2 + src/kcdatabase.cpp | 65 +++++------ 4 files changed, 138 insertions(+), 61 deletions(-) diff --git a/securityd_service/securityd_service/main.c b/securityd_service/securityd_service/main.c index 18283a8..5967484 100644 --- a/securityd_service/securityd_service/main.c +++ b/securityd_service/securityd_service/main.c @@ -28,7 +28,7 @@ #include #if DEBUG -#define LOG(...) syslog(LOG_NOTICE, ##__VA_ARGS__); +#define LOG(...) syslog(LOG_ERR, ##__VA_ARGS__); #else #define LOG(...) #endif @@ -43,6 +43,8 @@ const char * kb_home_path = "Library/Keychains"; const char * kb_user_bag = "user.kb"; const char * kb_stash_bag = "stash.kb"; +#define HEXBUF_LEN 2048 + typedef struct { uid_t uid; gid_t gid; @@ -340,34 +342,6 @@ _kb_delete_bag_on_disk(service_user_record_t * ur, const char * bag_file) } } -static void -_kb_migrate_old_bag_if_exists(service_user_record_t * ur) -{ - char session_file[PATH_MAX] = {}; - struct stat st_info = {}; - char * bag_file = _kb_copy_bag_filename(ur, kb_bag_type_user); - - if (bag_file) { - snprintf(session_file, sizeof(session_file), "/var/keybags/%i.kb", ur->uid); - - // if the bag_file does not exist - // check for the session_file and copy it into place - if (!_kb_bag_exists(ur, bag_file)) { - if (lstat(session_file, &st_info) == 0 && (S_ISREG(st_info.st_mode))) { - lchmod("/var/keybags", 0777); - lchmod(session_file, 0666); - _set_thread_credentials(ur); - _kb_verify_create_path(ur); - syslog(LOG_ERR, "migrating %s to %s", session_file, bag_file); - copyfile(session_file, bag_file, NULL, COPYFILE_ALL | COPYFILE_MOVE | COPYFILE_NOFOLLOW | COPYFILE_EXCL); - lchmod(bag_file, 0600); - _clear_thread_credentials(); - } - } - free(bag_file); - } -} - static int _kb_get_session_handle(service_context_t * context, keybag_handle_t * handle_out) { @@ -774,8 +748,91 @@ bool peer_has_entitlement(xpc_connection_t peer, const char * entitlement) return entitled; } +#if DEBUG +static char * +to_hex(char * dst, const void * src, size_t size) +{ + int notleading = 0; + + if ((size * 2) > HEXBUF_LEN) return NULL; + + uint8_t * buf = (uint8_t *)src; + register char *chp = dst; + *dst = '\0'; + if (size != 0) do { + if(notleading || *buf != '\0') { + if(!notleading && (*buf & 0xf0) == 0) { + sprintf(chp, "%.1x", * (unsigned char *) src); + chp += 1; + } + else { + sprintf(chp, "%.2x", * (unsigned char *) src); + chp += 2; + } + notleading = 1; + } + ++src; + } while (--size != 0); + return dst; +} +#endif // DEBUG + +static char * sel_to_char(uint64_t sel) +{ + switch (sel) { + case SERVICE_STASH_SET_KEY: + return "set_key"; + case SERVICE_STASH_GET_KEY: + return "get_key"; + case SERVICE_STASH_BLOB: + return "stash_blob"; + case SERVICE_KB_LOAD: + return "kb_load"; + case SERVICE_KB_UNLOCK: + return "kb_unlock"; + case SERVICE_KB_LOCK: + return "kb_lock"; + case SERVICE_KB_CHANGE_SECRET: + return "kb_change_secret"; + case SERVICE_KB_CREATE: + return "kb_create"; + case SERVICE_KB_IS_LOCKED: + return "kb_is_locked"; + case SERVICE_KB_RESET: + return "kb_reset"; + default: + return "unknown"; + } +} + +static char * err_to_char(int err) +{ + switch (err) { + case KB_Success: + return "success"; + case KB_GeneralError: + return "general error"; + case KB_BagNotFound: + return "bag not found"; + case KB_BagError: + return "bag error"; + case KB_BagNotLoaded: + return "bag not loaded"; + case KB_BagExists: + return "bag exists"; + case KB_InvalidSession: + return "invalid session"; + default: + return ""; + } +} + void service_peer_event_handler(xpc_connection_t connection, xpc_object_t event) { +#if DEBUG + char hexbuf1[HEXBUF_LEN]; + char hexbuf2[HEXBUF_LEN]; +#endif // DEBUG xpc_type_t type = xpc_get_type(event); if (type == XPC_TYPE_ERROR) { @@ -849,7 +906,13 @@ void service_peer_event_handler(xpc_connection_t connection, xpc_object_t event) } done: - LOG("selector: %llu, error: %x, secret_len: %zu, new_secret_len: %zu, sid: %d, suid: %d)", request, rc, secret_len, new_secret_len, context ? context->s_id : 0, context ? context->s_uid : 0); +#if DEBUG + 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); +#else + if (rc != 0) { + 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); + } +#endif xpc_dictionary_set_int64(reply, SERVICE_XPC_RC, rc); xpc_connection_send_message(connection, reply); xpc_release(reply); diff --git a/src/dbcrypto.cpp b/src/dbcrypto.cpp index 98b1caa..83d4469 100644 --- a/src/dbcrypto.cpp +++ b/src/dbcrypto.cpp @@ -135,6 +135,15 @@ void DatabaseCryptoCore::setup(const DbBlob *blob, CssmClient::Key master) mHaveMaster = true; } +bool DatabaseCryptoCore::get_encryption_key(CssmOwnedData &data) +{ + bool result = false; + if (isValid()) { + data = mEncryptionKey->keyData(); + result = true; + } + return result; +} // // Given a putative passphrase, determine whether that passphrase diff --git a/src/dbcrypto.h b/src/dbcrypto.h index 5970f51..dda04b4 100644 --- a/src/dbcrypto.h +++ b/src/dbcrypto.h @@ -68,6 +68,8 @@ public: static const uint32 managedAttributes = KeyBlob::managedAttributes; static const uint32 forcedAttributes = KeyBlob::forcedAttributes; + + bool get_encryption_key(CssmOwnedData &data); public: bool validatePassphrase(const CssmData &passphrase); diff --git a/src/kcdatabase.cpp b/src/kcdatabase.cpp index 68271fa..c9c1fe2 100644 --- a/src/kcdatabase.cpp +++ b/src/kcdatabase.cpp @@ -81,8 +81,30 @@ unlock_keybag(KeychainDbCommon & dbCommon, const void * secret, int secret_len) } } + if (rc != 0) { // if we just upgraded make sure we swap the encryption key to the password + if (!dbCommon.session().keybagGetState(session_keybag_check_master_key)) { + CssmAutoData encKey(Allocator::standard(Allocator::sensitive)); + dbCommon.get_encryption_key(encKey); + if ((rc = service_client_kb_unlock(&context, encKey.data(), (int)encKey.length())) == 0) { + rc = service_client_kb_change_secret(&context, encKey.data(), (int)encKey.length(), secret, secret_len); + } + + if (rc != 0) { // if a login.keychain password exists but doesnt on the keybag update it + bool no_pin = false; + if ((secret_len > 0) && service_client_kb_is_locked(&context, NULL, &no_pin) == 0) { + if (no_pin) { + syslog(LOG_ERR, "Updating iCloud keychain passphrase for uid %d", dbCommon.session().originatorUid()); + service_client_kb_change_secret(&context, NULL, 0, secret, secret_len); + } + } + } + } // session_keybag_check_master_key + } + if (rc == 0) { - dbCommon.session().keybagSetState(session_keybag_unlocked|session_keybag_loaded); + dbCommon.session().keybagSetState(session_keybag_unlocked|session_keybag_loaded|session_keybag_check_master_key); + } else { + syslog(LOG_ERR, "Failed to unlock iCloud keychain for uid %d", dbCommon.session().originatorUid()); } return rc; @@ -528,7 +550,8 @@ void KeychainDatabase::makeUnlocked(const AccessCredentials *cred) // void KeychainDatabase::stashDbCheck() { - CssmAutoData data(Allocator::standard(Allocator::sensitive)); + CssmAutoData masterKey(Allocator::standard(Allocator::sensitive)); + CssmAutoData encKey(Allocator::standard(Allocator::sensitive)); // Fetch the key int rc = 0; @@ -538,7 +561,7 @@ void KeychainDatabase::stashDbCheck() rc = service_client_stash_get_key(&context, &stash_key, &stash_key_len); if (rc == 0) { if (stash_key) { - data.copy(CssmData((void *)stash_key,stash_key_len)); + masterKey.copy(CssmData((void *)stash_key,stash_key_len)); memset(stash_key, 0, stash_key_len); free(stash_key); } @@ -550,7 +573,7 @@ void KeychainDatabase::stashDbCheck() StLock _(common()); // Now establish it as the keychain master key - CssmClient::Key key(Server::csp(), data.get()); + CssmClient::Key key(Server::csp(), masterKey.get()); CssmKey::Header &hdr = key.header(); hdr.keyClass(CSSM_KEYCLASS_SESSION_KEY); hdr.algorithm(CSSM_ALGID_3DES_3KEY_EDE); @@ -561,12 +584,14 @@ void KeychainDatabase::stashDbCheck() if (!decode()) CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED); + + common().get_encryption_key(encKey); } - // when upgrading from pre-10.9 create a keybag if it doesn't exist with the master secret + // when upgrading from pre-10.9 create a keybag if it doesn't exist with the encryption key // only do this after we have verified the master key unlocks the login.keychain if (service_client_kb_load(&context) == KB_BagNotFound) { - service_client_kb_create(&context, data.data(), (int)data.length()); + service_client_kb_create(&context, encKey.data(), (int)encKey.length()); } } @@ -620,7 +645,7 @@ void KeychainDatabase::makeUnlocked(const CssmData &passphrase) if (common().isLoginKeychain()) { bool locked = false; service_context_t context = common().session().get_current_service_context(); - if ((service_client_kb_is_locked(&context, &locked, NULL) == 0) && locked) { + if (!common().session().keybagGetState(session_keybag_check_master_key) || ((service_client_kb_is_locked(&context, &locked, NULL) == 0) && locked)) { unlock_keybag(common(), passphrase.data(), (int)passphrase.length()); } } @@ -640,30 +665,8 @@ bool KeychainDatabase::decode(const CssmData &passphrase) assert(mBlob); common().setup(mBlob, passphrase); bool success = decode(); - if (success) { - if (common().isLoginKeychain() && (unlock_keybag(common(), passphrase.data(), (int)passphrase.length()) != 0)) { - service_context_t context = common().session().get_current_service_context(); - // check to see if it was locked with the master key if so change the secret to the passphrase - if (!common().session().keybagGetState(session_keybag_check_master_key)) { - - CssmAutoData key(Allocator::standard(Allocator::sensitive)); - key = common().masterKey()->keyData(); - if (service_client_kb_unlock(&context, key.data(), (int)key.length()) == 0) { - service_client_kb_change_secret(&context, key.data(), (int)key.length(), passphrase.data(), (int)passphrase.length()); - } - common().session().keybagSetState(session_keybag_check_master_key); - } - - bool no_pin = false; - if (service_client_kb_is_locked(&context, NULL, &no_pin) == 0) { - if ((passphrase.length() > 0) && no_pin) { - syslog(LOG_ERR, "Updating passphrase for your iCloud keychain"); - service_client_kb_change_secret(&context, NULL, 0, passphrase.data(), (int)passphrase.length()); - } else { - syslog(LOG_ERR, "The passphrase for your login.keychain and your iCloud keychain are out of sync"); - } - } - } + if (success && common().isLoginKeychain()) { + unlock_keybag(common(), passphrase.data(), (int)passphrase.length()); } return success; } -- 2.45.2