|  | /* | 
|  | * 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(const KeymasterDevices& kmDevices, | 
|  | SecurityLevel minimalAllowedSecurityLevelForNewKeys) | 
|  | : 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); | 
|  | } | 
|  |  | 
|  | 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<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; | 
|  |  | 
|  | 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<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 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<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()); | 
|  | } | 
|  |  | 
|  | 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<ErrorCode> 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<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; | 
|  | } | 
|  |  | 
|  | 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 |