| /* | 
 |  * 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, SecurityLevel(i)); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | 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 |