| /* |
| * 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 <dirent.h> |
| #include <fcntl.h> |
| |
| #include <openssl/bio.h> |
| |
| #include <utils/String16.h> |
| #include <utils/String8.h> |
| |
| #include <android-base/scopeguard.h> |
| #include <android/hardware/keymaster/3.0/IKeymasterDevice.h> |
| #include <android/security/keystore/IKeystoreService.h> |
| #include <log/log_event_list.h> |
| |
| #include <private/android_logger.h> |
| |
| #include "keystore_utils.h" |
| #include "permissions.h" |
| #include <keystore/keystore_hidl_support.h> |
| |
| #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(Entropy* entropy, const KeymasterDevices& kmDevices, |
| SecurityLevel minimalAllowedSecurityLevelForNewKeys) |
| : mEntropy(entropy), |
| mAllowNewFallback(minimalAllowedSecurityLevelForNewKeys == SecurityLevel::SOFTWARE), |
| mConfirmationManager(new ConfirmationManager(this)) { |
| memset(&mMetaData, '\0', sizeof(mMetaData)); |
| |
| static_assert(std::tuple_size<std::decay_t<decltype(kmDevices)>>::value == |
| std::tuple_size<decltype(mKmDevices)>::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<KeymasterWorker>(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, mEntropy); |
| } |
| |
| 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, mEntropy); |
| } |
| |
| ResponseCode KeyStore::readMasterKey(const android::String8& pw, uid_t userId) { |
| auto userState = mUserStateDB.getUserState(userId); |
| return userState->readMasterKey(pw, mEntropy); |
| } |
| |
| 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<KeyBlobEntry> 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<android::String16> aliases; |
| |
| // DO NOT |
| // move |
| // auto userState = userStateDB_.getUserState(userId); |
| // here, in an attempt to replace userStateDB_.getUserState(userId) with userState. |
| // userState is a proxy that holds a lock which may 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 only briefly grabs the lock. |
| // Grabbing the user state lock after the list call is also save since workers cannot grab |
| // blob entry locks. |
| |
| auto userState = mUserStateDB.getUserState(userId); |
| std::string userDirName = userState->getUserDirName(); |
| auto encryptionKey = userState->getEncryptionKey(); |
| auto state = userState->getState(); |
| // unlock the user state |
| userState = {}; |
| |
| ResponseCode rc; |
| std::list<LockedKeyBlobEntry> 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 hold a lock which must be relinqhished before list is called. This scope |
| // prevents deadlocks. |
| auto userState = mUserStateDB.getUserState(userId); |
| if (!userState) { |
| return true; |
| } |
| userDirName = userState->getUserDirName(); |
| } |
| |
| ResponseCode rc; |
| std::list<LockedKeyBlobEntry> 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<ResponseCode, Blob, Blob> KeyStore::get(const LockedKeyBlobEntry& blobfile) { |
| std::tuple<ResponseCode, Blob, Blob> 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(), mEntropy); |
| } |
| |
| 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), [alias, uid](Return<ErrorCode> rc) { |
| auto ret = KS_HANDLE_HIDL_ERROR(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<ResponseCode, Blob, Blob, LockedKeyBlobEntry> |
| KeyStore::getKeyForName(const android::String8& keyName, const uid_t uid, const BlobType type) { |
| std::tuple<ResponseCode, Blob, Blob, LockedKeyBlobEntry> 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; |
| } |
| |
| struct BIO_Delete { |
| void operator()(BIO* p) const { BIO_free(p); } |
| }; |
| typedef std::unique_ptr<BIO, BIO_Delete> Unique_BIO; |
| |
| 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<IBinder>& who) { |
| for (unsigned i = 0; i < mKmDevices.size(); ++i) { |
| if (mKmDevices[SecurityLevel(i)]) mKmDevices[SecurityLevel(i)]->binderDied(who); |
| } |
| getConfirmationManager().binderDied(who); |
| } |
| |
| } // namespace keystore |