/*
* 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;
}
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