/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "keystore" #include "KeyStore.h" #include #include #include #include #include #include #include #include #include #include #include "keystore_utils.h" #include "permissions.h" #include #include "keymaster_worker.h" namespace keystore { const char* KeyStore::kOldMasterKey = ".masterkey"; const char* KeyStore::kMetaDataFile = ".metadata"; const android::String16 KeyStore::kRsaKeyType("RSA"); const android::String16 KeyStore::kEcKeyType("EC"); using android::String8; KeyStore::KeyStore(const KeymasterDevices& kmDevices, SecurityLevel minimalAllowedSecurityLevelForNewKeys) : mAllowNewFallback(minimalAllowedSecurityLevelForNewKeys == SecurityLevel::SOFTWARE), mConfirmationManager(new ConfirmationManager(this)) { memset(&mMetaData, '\0', sizeof(mMetaData)); static_assert(std::tuple_size>::value == std::tuple_size::value, "KmasterDevices and KeymasterWorkers must have the same size"); for (size_t i = 0; i < kmDevices.size(); ++i) { if (kmDevices[SecurityLevel(i)]) { mKmDevices[SecurityLevel(i)] = std::make_shared(kmDevices[SecurityLevel(i)], this); } } } KeyStore::~KeyStore() { } ResponseCode KeyStore::initialize() { readMetaData(); if (upgradeKeystore()) { writeMetaData(); } return ResponseCode::NO_ERROR; } ResponseCode KeyStore::initializeUser(const android::String8& pw, uid_t userId) { auto userState = mUserStateDB.getUserState(userId); return userState->initialize(pw); } ResponseCode KeyStore::copyMasterKey(uid_t srcUser, uid_t dstUser) { auto userState = mUserStateDB.getUserState(dstUser); auto initState = mUserStateDB.getUserState(srcUser); return userState->copyMasterKey(&initState); } ResponseCode KeyStore::writeMasterKey(const android::String8& pw, uid_t userId) { auto userState = mUserStateDB.getUserState(userId); return userState->writeMasterKey(pw); } ResponseCode KeyStore::readMasterKey(const android::String8& pw, uid_t userId) { auto userState = mUserStateDB.getUserState(userId); return userState->readMasterKey(pw); } LockedKeyBlobEntry KeyStore::getLockedBlobEntryIfNotExists(const std::string& alias, uid_t uid) { KeyBlobEntry kbe(alias, mUserStateDB.getUserStateByUid(uid)->getUserDirName(), uid); auto result = LockedKeyBlobEntry::get(std::move(kbe)); if (result->hasKeyBlob()) return {}; return result; } std::optional KeyStore::getBlobEntryIfExists(const std::string& alias, uid_t uid) { KeyBlobEntry kbe(alias, mUserStateDB.getUserStateByUid(uid)->getUserDirName(), uid); if (kbe.hasKeyBlob()) return kbe; // If this is one of the legacy UID->UID mappings, use it. uid_t euid = get_keystore_euid(uid); if (euid != uid) { kbe = KeyBlobEntry(alias, mUserStateDB.getUserStateByUid(euid)->getUserDirName(), euid); if (kbe.hasKeyBlob()) return kbe; } // They might be using a granted key. auto grant = mGrants.get(uid, alias); if (grant) { kbe = grant->entry_; if (kbe.hasKeyBlob()) return kbe; } return {}; } LockedKeyBlobEntry KeyStore::getLockedBlobEntryIfExists(const std::string& alias, uid_t uid) { auto blobentry = getBlobEntryIfExists(alias, uid); if (!blobentry) return {}; LockedKeyBlobEntry lockedentry = LockedKeyBlobEntry::get(std::move(*blobentry)); if (!lockedentry || !lockedentry->hasKeyBlob()) return {}; return lockedentry; } void KeyStore::resetUser(uid_t userId, bool keepUnenryptedEntries) { android::String8 prefix(""); android::Vector aliases; auto userState = mUserStateDB.getUserState(userId); std::string userDirName = userState->getUserDirName(); auto encryptionKey = userState->getEncryptionKey(); auto state = userState->getState(); // userState is a proxy that holds a lock which may be required by a worker. // LockedKeyBlobEntry::list has a fence that waits until all workers have finished which may // not happen if a user state lock is held. The following line relinquishes the lock. userState = {}; ResponseCode rc; std::list matches; // must not be called by a keymaster worker. List waits for workers to relinquish all access // to blob entries std::tie(rc, matches) = LockedKeyBlobEntry::list(userDirName); if (rc != ResponseCode::NO_ERROR) { return; } for (LockedKeyBlobEntry& lockedEntry : matches) { bool shouldDelete = true; if (keepUnenryptedEntries) { Blob blob; Blob charBlob; ResponseCode rc; std::tie(rc, blob, charBlob) = lockedEntry.readBlobs(encryptionKey, state); switch (rc) { case ResponseCode::SYSTEM_ERROR: case ResponseCode::VALUE_CORRUPTED: // If we can't read blobs, delete them. shouldDelete = true; break; case ResponseCode::NO_ERROR: case ResponseCode::LOCKED: // Delete encrypted blobs but keep unencrypted blobs and super-encrypted blobs. We // need to keep super-encrypted blobs so we can report that the user is // unauthenticated if a caller tries to use them, rather than reporting that they // don't exist. shouldDelete = blob.isEncrypted(); break; default: ALOGE("Got unexpected return code %d from readBlobs", rc); // This shouldn't happen. To be on the safe side, delete it. shouldDelete = true; break; } } if (shouldDelete) { del(lockedEntry); } } userState = mUserStateDB.getUserState(userId); if (!userState->deleteMasterKey()) { ALOGE("Failed to delete user %d's master key", userId); } if (!keepUnenryptedEntries) { if (!userState->reset()) { ALOGE("Failed to remove user %d's directory", userId); } } } bool KeyStore::isEmpty(uid_t userId) const { std::string userDirName; { // userState holds a lock which must be relinquished before list is called. This scope // prevents deadlocks. auto userState = mUserStateDB.getUserState(userId); if (!userState) { return true; } userDirName = userState->getUserDirName(); } ResponseCode rc; std::list matches; // must not be called by a keymaster worker. List waits for workers to relinquish all access // to blob entries std::tie(rc, matches) = LockedKeyBlobEntry::list(userDirName); return rc == ResponseCode::SYSTEM_ERROR || matches.size() == 0; } void KeyStore::lock(uid_t userId) { auto userState = mUserStateDB.getUserState(userId); userState->zeroizeMasterKeysInMemory(); userState->setState(STATE_LOCKED); } static void maybeLogKeyIntegrityViolation(const LockedKeyBlobEntry& lockedEntry, const BlobType type) { if (!__android_log_security() || (type != TYPE_KEY_PAIR && type != TYPE_KEYMASTER_10)) return; log_key_integrity_violation(lockedEntry->alias().c_str(), lockedEntry->uid()); } std::tuple KeyStore::get(const LockedKeyBlobEntry& blobfile) { std::tuple result; uid_t userId = get_user_id(blobfile->uid()); Blob& keyBlob = std::get<1>(result); ResponseCode& rc = std::get<0>(result); auto userState = mUserStateDB.getUserState(userId); BlobType type = BlobType::TYPE_ANY; auto logOnScopeExit = android::base::make_scope_guard([&] { if (rc == ResponseCode::VALUE_CORRUPTED) { maybeLogKeyIntegrityViolation(blobfile, type); } }); result = blobfile.readBlobs(userState->getEncryptionKey(), userState->getState()); if (rc != ResponseCode::NO_ERROR) { return result; } // update the type for logging (see scope_guard above) type = keyBlob.getType(); const uint8_t version = keyBlob.getVersion(); if (version < CURRENT_BLOB_VERSION) { /* If we upgrade the key, we need to write it to disk again. Then * it must be read it again since the blob is encrypted each time * it's written. */ if (upgradeBlob(&keyBlob, version)) { if ((rc = this->put(blobfile, keyBlob, {})) != ResponseCode::NO_ERROR || (result = blobfile.readBlobs(userState->getEncryptionKey(), userState->getState()), rc) != ResponseCode::NO_ERROR) { return result; } } } return result; } ResponseCode KeyStore::put(const LockedKeyBlobEntry& blobfile, Blob keyBlob, Blob characteristicsBlob) { auto userState = mUserStateDB.getUserStateByUid(blobfile->uid()); return blobfile.writeBlobs(std::move(keyBlob), std::move(characteristicsBlob), userState->getEncryptionKey(), userState->getState()); } ResponseCode KeyStore::del(const LockedKeyBlobEntry& blobfile) { Blob keyBlob; Blob charactaristicsBlob; ResponseCode rc; uid_t uid = blobfile->uid(); std::string alias = blobfile->alias(); std::tie(rc, keyBlob, charactaristicsBlob) = get(blobfile); // after getting the blob from the file system we scrub the filesystem. mGrants.removeAllGrantsToKey(uid, alias); auto result = blobfile.deleteBlobs(); if (rc != ResponseCode::NO_ERROR) { LOG(ERROR) << "get keyblob failed " << int(rc); return rc; } // if we got the blob successfully, we try and delete it from the keymaster device auto dev = getDevice(keyBlob); if (keyBlob.getType() == ::TYPE_KEYMASTER_10) { dev->deleteKey(blob2hidlVec(keyBlob), [dev, alias, uid](Return rc) { auto ret = KS_HANDLE_HIDL_ERROR(dev, rc); // A device doesn't have to implement delete_key. bool success = ret == ErrorCode::OK || ret == ErrorCode::UNIMPLEMENTED; if (__android_log_security()) { android_log_event_list(SEC_TAG_KEY_DESTROYED) << int32_t(success) << alias << int32_t(uid) << LOG_ID_SECURITY; } if (!success) { LOG(ERROR) << "Keymaster delete for key " << alias << " of uid " << uid << " failed"; } }); } return result; } std::string KeyStore::addGrant(const LockedKeyBlobEntry& blobfile, uid_t granteeUid) { return mGrants.put(granteeUid, blobfile); } bool KeyStore::removeGrant(const LockedKeyBlobEntry& blobfile, const uid_t granteeUid) { return mGrants.removeByFileAlias(granteeUid, blobfile); } void KeyStore::removeAllGrantsToUid(const uid_t granteeUid) { mGrants.removeAllGrantsToUid(granteeUid); } bool KeyStore::isHardwareBacked(const android::String16& keyType) const { // if strongbox device is present TEE must also be present and of sufficiently high version // to support all keys in hardware if (getDevice(SecurityLevel::STRONGBOX)) return true; if (!getDevice(SecurityLevel::TRUSTED_ENVIRONMENT)) { ALOGW("can't get keymaster device"); return false; } auto version = getDevice(SecurityLevel::TRUSTED_ENVIRONMENT)->halVersion(); if (keyType == kRsaKeyType) return true; // All versions support RSA return keyType == kEcKeyType && version.supportsEc; } std::tuple KeyStore::getKeyForName(const android::String8& keyName, const uid_t uid, const BlobType type) { std::tuple result; auto& [rc, keyBlob, charBlob, lockedEntry] = result; lockedEntry = getLockedBlobEntryIfExists(keyName.string(), uid); if (!lockedEntry) return rc = ResponseCode::KEY_NOT_FOUND, std::move(result); std::tie(rc, keyBlob, charBlob) = get(lockedEntry); if (rc == ResponseCode::NO_ERROR) { if (keyBlob.getType() != type) return rc = ResponseCode::KEY_NOT_FOUND, std::move(result); } return result; } bool KeyStore::upgradeBlob(Blob* blob, const uint8_t oldVersion) { bool updated = false; uint8_t version = oldVersion; if (!blob || !(*blob)) return false; /* From V0 -> V1: All old types were unknown */ if (version == 0) { ALOGE("Failed to upgrade key blob. Ancient blob version 0 is no longer supported"); return false; } /* From V1 -> V2: All old keys were encrypted */ if (version == 1) { ALOGV("upgrading to version 2"); blob->setEncrypted(true); version = 2; updated = true; } /* * If we've updated, set the key blob to the right version * and write it. */ if (updated) { blob->setVersion(version); } return updated; } void KeyStore::readMetaData() { int in = TEMP_FAILURE_RETRY(open(kMetaDataFile, O_RDONLY)); if (in < 0) { return; } size_t fileLength = readFully(in, (uint8_t*)&mMetaData, sizeof(mMetaData)); if (fileLength != sizeof(mMetaData)) { ALOGI("Metadata file is %zd bytes (%zd experted); upgrade?", fileLength, sizeof(mMetaData)); } close(in); } void KeyStore::writeMetaData() { const char* tmpFileName = ".metadata.tmp"; int out = TEMP_FAILURE_RETRY(open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)); if (out < 0) { ALOGE("couldn't write metadata file: %s", strerror(errno)); return; } size_t fileLength = writeFully(out, (uint8_t*)&mMetaData, sizeof(mMetaData)); if (fileLength != sizeof(mMetaData)) { ALOGI("Could only write %zd bytes to metadata file (%zd expected)", fileLength, sizeof(mMetaData)); } close(out); rename(tmpFileName, kMetaDataFile); } bool KeyStore::upgradeKeystore() { bool upgraded = false; if (mMetaData.version == 0) { auto userState = getUserStateDB().getUserStateByUid(0); // Initialize first so the directory is made. userState->initialize(); // Migrate the old .masterkey file to user 0. if (access(kOldMasterKey, R_OK) == 0) { if (rename(kOldMasterKey, userState->getMasterKeyFileName().c_str()) < 0) { ALOGE("couldn't migrate old masterkey: %s", strerror(errno)); return false; } } // Initialize again in case we had a key. userState->initialize(); // Try to migrate existing keys. DIR* dir = opendir("."); if (!dir) { // Give up now; maybe we can upgrade later. ALOGE("couldn't open keystore's directory; something is wrong"); return false; } struct dirent* file; while ((file = readdir(dir)) != nullptr) { // We only care about files. if (file->d_type != DT_REG) { continue; } // Skip anything that starts with a "." if (file->d_name[0] == '.') { continue; } // Find the current file's user. char* end; unsigned long thisUid = strtoul(file->d_name, &end, 10); if (end[0] != '_' || end[1] == 0) { continue; } auto otherUser = getUserStateDB().getUserStateByUid(thisUid); if (otherUser->getUserId() != 0) { unlinkat(dirfd(dir), file->d_name, 0); } // Rename the file into user directory. DIR* otherdir = opendir(otherUser->getUserDirName().c_str()); if (otherdir == nullptr) { ALOGW("couldn't open user directory for rename"); continue; } if (renameat(dirfd(dir), file->d_name, dirfd(otherdir), file->d_name) < 0) { ALOGW("couldn't rename blob: %s: %s", file->d_name, strerror(errno)); } closedir(otherdir); } closedir(dir); mMetaData.version = 1; upgraded = true; } return upgraded; } void KeyStore::binderDied(const ::android::wp& who) { for (unsigned i = 0; i < mKmDevices.size(); ++i) { if (mKmDevices[SecurityLevel(i)]) mKmDevices[SecurityLevel(i)]->binderDied(who); } getConfirmationManager().binderDied(who); } } // namespace keystore