ff3d7f4b83
This patch transitions keystore a threading model with one dispatcher thread and one worker thread per keymaster instance, i.e. fallback, TEE, Strongbox (if available). Singleton objects, such as the user state database, the enforcement policy, and grant database have been moved to KeyStore and were made concurrency safe. Other noteworthy changes in this patch: * Cached key characteristics. The key characteristics file used to hold a limited set of parameters used generate or import the key. This patch introduces a new blob type that holds full characteristics as returned by generate, import, or getKeyCharacteristics, with the original parameters mixed into the software enforced list. When keystore encounters a lagacy characteristics file it will grab the characteristics from keymaster, merge them with the cached parameters, and update the cache file to the new format. If keystore encounters the new cache no call to keymaster will be made for retrieving the key characteristics. * Changed semantic of list. The list call takes a prefix used for filtering key entries. By the old semantic, list would return a list of aliases stripped of the given prefix. By the new semantic list always returns a filtered list of full alias string. Callers may strip prefixes if they are so inclined. * Entertain per keymaster instance operation maps. With the introduction of Strongbox keystore had to deal with multiple keymaster instances. But until now it would entertain a single operations map. Keystore also enforces the invariant that no more than 15 operation slots are used so there is always a free slot available for vold. With a single operation map, this means no more than 15 slots can ever be used although with TEE and Strongbox there are a total of 32 slots. With strongbox implementation that have significantly fewer slots we see another effect of the single operation map. If a slot needs to be freed on Stronbox but the oldest operations are on TEE, the latter will be unnecessarily pruned before a Strongbox slot is freed up. With this patch each keymaster instance has its own operation map and pruning is performed on a per keymaster instance basis. * Introduce KeyBlobEntries which are independent from files. To allow concurrent access to the key blob data base, entries can be individually locked so that operations on entries become atomic. LockedKeyBlobEntries are move only objects that track ownership of an Entry on the stack or in functor object representing keymaster worker requests. Entries must only be locked by the dispatcher Thread. Worker threads can only be granted access to a LockedKeyBlobEntry by the dispatcher thread. This allows the dispatcher thread to execute a barrier that waits until all locks held by workers have been relinquished to perform blob database maintenance operations, e.g., clearing a uid of all entries. * Verification tokens are now acquired asynchronously. When a begin operation requires a verification token a request is submitted to the other keymaster worker while the begin call returns. When the operation commences with update or finish, we block until the verification token becomes available. As of this patch the keystore IPC interface is still synchronous. That is, the dispatcher thread dispatches a request to a worker and then waits until the worker has finished. In a followup patch the IPC interface shall be made asynchronous so that multiple requests may be in flight. Test: Ran full CTS test suite atest android.keystore.cts Bug: 111443219 Bug: 110495056 Change-Id: I305e28d784295a0095a34810d83202f7423498bd
216 lines
8.4 KiB
C++
216 lines
8.4 KiB
C++
/*
|
|
* Copyright (C) 2017 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 "ConfirmationManager"
|
|
|
|
#include "confirmation_manager.h"
|
|
|
|
#include <android/hardware/confirmationui/1.0/IConfirmationResultCallback.h>
|
|
#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
|
|
#include <android/hardware/confirmationui/1.0/types.h>
|
|
#include <android/security/BpConfirmationPromptCallback.h>
|
|
#include <binder/BpBinder.h>
|
|
#include <binder/IPCThreadState.h>
|
|
#include <binder/Parcel.h>
|
|
|
|
#include "keystore_aidl_hidl_marshalling_utils.h"
|
|
|
|
namespace keystore {
|
|
|
|
using android::IBinder;
|
|
using android::sp;
|
|
using android::String16;
|
|
using android::String8;
|
|
using android::wp;
|
|
using android::binder::Status;
|
|
using android::hardware::hidl_vec;
|
|
using android::hardware::Return;
|
|
using android::hardware::confirmationui::V1_0::IConfirmationResultCallback;
|
|
using android::hardware::confirmationui::V1_0::IConfirmationUI;
|
|
using android::hardware::confirmationui::V1_0::UIOption;
|
|
|
|
using android::security::BpConfirmationPromptCallback;
|
|
using std::lock_guard;
|
|
using std::mutex;
|
|
using std::vector;
|
|
|
|
ConfirmationManager::ConfirmationManager(IBinder::DeathRecipient* deathRecipient)
|
|
: IConfirmationResultCallback(), mDeathRecipient(deathRecipient) {}
|
|
|
|
// Called by keystore main thread.
|
|
Status ConfirmationManager::presentConfirmationPrompt(const sp<IBinder>& listener,
|
|
const String16& promptText,
|
|
const hidl_vec<uint8_t>& extraData,
|
|
const String16& locale, int uiOptionsAsFlags,
|
|
int32_t* aidl_return) {
|
|
lock_guard<mutex> lock(mMutex);
|
|
|
|
if (mCurrentListener != nullptr) {
|
|
*aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OperationPending);
|
|
return Status::ok();
|
|
}
|
|
|
|
sp<IConfirmationUI> confirmationUI = IConfirmationUI::tryGetService();
|
|
if (confirmationUI == nullptr) {
|
|
ALOGW("Error getting confirmationUI service\n");
|
|
*aidl_return = static_cast<int32_t>(ConfirmationResponseCode::Unimplemented);
|
|
return Status::ok();
|
|
}
|
|
|
|
uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
|
|
if (!mRateLimiting.tryPrompt(callingUid)) {
|
|
*aidl_return = static_cast<int32_t>(ConfirmationResponseCode::SystemError);
|
|
return Status::ok();
|
|
}
|
|
|
|
String8 promptText8(promptText);
|
|
String8 locale8(locale);
|
|
vector<UIOption> uiOptionsVector;
|
|
for (int n = 0; n < 32; n++) {
|
|
if (uiOptionsAsFlags & (1 << n)) {
|
|
uiOptionsVector.push_back(UIOption(n));
|
|
}
|
|
}
|
|
ConfirmationResponseCode responseCode;
|
|
responseCode = confirmationUI->promptUserConfirmation(sp<IConfirmationResultCallback>(this),
|
|
promptText8.string(), extraData,
|
|
locale8.string(), uiOptionsVector);
|
|
if (responseCode != ConfirmationResponseCode::OK) {
|
|
ALOGW("Unexpecxted responseCode %d from promptUserConfirmation\n", responseCode);
|
|
*aidl_return = static_cast<int32_t>(responseCode);
|
|
return Status::ok();
|
|
}
|
|
|
|
listener->linkToDeath(mDeathRecipient);
|
|
confirmationUI->linkToDeath(this, 0);
|
|
mCurrentListener = listener;
|
|
mCurrentConfirmationUI = confirmationUI;
|
|
|
|
*aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK);
|
|
return Status::ok();
|
|
}
|
|
|
|
// Called by keystore main thread.
|
|
Status ConfirmationManager::cancelConfirmationPrompt(const sp<IBinder>& listener,
|
|
int32_t* aidl_return) {
|
|
mMutex.lock();
|
|
if (mCurrentListener != listener) {
|
|
// If the prompt was displayed by another application, return
|
|
// OperationPending.
|
|
mMutex.unlock();
|
|
*aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OperationPending);
|
|
return Status::ok();
|
|
}
|
|
mMutex.unlock();
|
|
|
|
finalizeTransaction(ConfirmationResponseCode::Aborted, {}, true);
|
|
|
|
*aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK);
|
|
return Status::ok();
|
|
}
|
|
|
|
// Called by keystore main thread.
|
|
Status ConfirmationManager::isConfirmationPromptSupported(bool* aidl_return) {
|
|
sp<IConfirmationUI> confirmationUI = IConfirmationUI::tryGetService();
|
|
if (confirmationUI == nullptr) {
|
|
ALOGW("Error getting confirmationUI service\n");
|
|
*aidl_return = false;
|
|
return Status::ok();
|
|
}
|
|
|
|
*aidl_return = true;
|
|
return Status::ok();
|
|
}
|
|
|
|
void ConfirmationManager::finalizeTransaction(ConfirmationResponseCode responseCode,
|
|
hidl_vec<uint8_t> dataThatWasConfirmed,
|
|
bool callAbortOnHal) {
|
|
// Note that confirmationUI->abort() may make the remote HAL process do an IPC call back
|
|
// into our process resulting in confirmationResultCallback() to be called... this in turn
|
|
// calls finalizeTransaction(). So we have to be careful a) not holding any locks;
|
|
// and b) ensure state has been cleared; before doing this...
|
|
|
|
mMutex.lock();
|
|
mRateLimiting.processResult(responseCode);
|
|
sp<IBinder> listener = mCurrentListener;
|
|
if (mCurrentListener != nullptr) {
|
|
mCurrentListener->unlinkToDeath(mDeathRecipient);
|
|
mCurrentListener = nullptr;
|
|
}
|
|
sp<IConfirmationUI> confirmationUI = mCurrentConfirmationUI;
|
|
if (mCurrentConfirmationUI != nullptr) {
|
|
mCurrentConfirmationUI->unlinkToDeath(this);
|
|
mCurrentConfirmationUI = nullptr;
|
|
}
|
|
mMutex.unlock();
|
|
|
|
// Tell the HAL to shut down the confirmation dialog, if requested.
|
|
if (confirmationUI != nullptr && callAbortOnHal) {
|
|
confirmationUI->abort();
|
|
}
|
|
|
|
// Deliver result to the application that started the operation.
|
|
if (listener != nullptr) {
|
|
sp<BpConfirmationPromptCallback> obj = new BpConfirmationPromptCallback(listener);
|
|
Status status = obj->onConfirmationPromptCompleted(static_cast<int32_t>(responseCode),
|
|
dataThatWasConfirmed);
|
|
if (!status.isOk()) {
|
|
ALOGW("Error sending onConfirmationPromptCompleted - status: %s\n",
|
|
status.toString8().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called by hwbinder thread (not keystore main thread).
|
|
Return<void> ConfirmationManager::result(ConfirmationResponseCode responseCode,
|
|
const hidl_vec<uint8_t>& dataThatWasConfirmed,
|
|
const hidl_vec<uint8_t>& confirmationToken) {
|
|
finalizeTransaction(responseCode, dataThatWasConfirmed, false);
|
|
lock_guard<mutex> lock(mMutex);
|
|
mLatestConfirmationToken = confirmationToken;
|
|
return Return<void>();
|
|
}
|
|
|
|
// Called by keystore main thread or keymaster worker
|
|
hidl_vec<uint8_t> ConfirmationManager::getLatestConfirmationToken() {
|
|
lock_guard<mutex> lock(mMutex);
|
|
return mLatestConfirmationToken;
|
|
}
|
|
|
|
void ConfirmationManager::binderDied(const wp<IBinder>& who) {
|
|
// This is also called for other binders so need to check it's for
|
|
// us before acting on it.
|
|
mMutex.lock();
|
|
if (who == mCurrentListener) {
|
|
// Clear this so we don't call back into the already dead
|
|
// binder in finalizeTransaction().
|
|
mCurrentListener->unlinkToDeath(mDeathRecipient);
|
|
mCurrentListener = nullptr;
|
|
mMutex.unlock();
|
|
ALOGW("The process which requested the confirmation dialog died.\n");
|
|
finalizeTransaction(ConfirmationResponseCode::SystemError, {}, true);
|
|
} else {
|
|
mMutex.unlock();
|
|
}
|
|
}
|
|
|
|
void ConfirmationManager::serviceDied(uint64_t /* cookie */,
|
|
const wp<android::hidl::base::V1_0::IBase>& /* who */) {
|
|
ALOGW("The ConfirmationUI HAL died.\n");
|
|
finalizeTransaction(ConfirmationResponseCode::SystemError, {}, false);
|
|
}
|
|
|
|
} // namespace keystore
|