Added listUidsOfCredentialBoundKeys method to AIDL

Bug: b/112321280
Test: add auth bound keys from an app
Test: adb shell "su 1000 keystore_cli_v2 list-apps-with-keys"
Test: see uid of app listed
Test: $ ./keystore/tests/list_auth_bound_keys_test.sh
Change-Id: Id6739ea17e566f9aa51f1bd2d5a0715f1020b644
This commit is contained in:
Rob Barnes 2018-11-08 15:44:10 -07:00
parent adde739791
commit eb7f79b832
5 changed files with 198 additions and 4 deletions

View file

@ -75,4 +75,5 @@ interface IKeystoreService {
int cancelConfirmationPrompt(IBinder listener);
boolean isConfirmationPromptSupported();
int onKeyguardVisibilityChanged(in boolean isShowing, in int userId);
int listUidsOfAuthBoundKeys(out int[] uids);
}

View file

@ -276,6 +276,79 @@ Status KeyStoreService::list(const String16& prefix, int32_t targetUid,
return Status::ok();
}
/*
* This method will return the uids of all auth bound keys for the calling user.
* This is intended to be used for alerting the user about which apps will be affected
* if the password/pin is removed. Only allowed to be called by system.
* The output is bound by the initial size of uidsOut to be compatible with Java.
*/
Status KeyStoreService::listUidsOfAuthBoundKeys(::std::vector<int32_t>* uidsOut,
int32_t* aidl_return) {
const int32_t callingUid = IPCThreadState::self()->getCallingUid();
const int32_t userId = get_user_id(callingUid);
const int32_t appId = get_app_id(callingUid);
if (appId != AID_SYSTEM) {
ALOGE("Permission listUidsOfAuthBoundKeys denied for aid %d", appId);
*aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
return Status::ok();
}
const String8 prefix8("");
auto userState = mKeyStore->getUserStateDB().getUserState(userId);
const std::string userDirName = userState->getUserDirName();
auto encryptionKey = userState->getEncryptionKey();
auto state = userState->getState();
// unlock the user state
userState = {};
ResponseCode rc;
std::list<LockedKeyBlobEntry> internal_matches;
std::tie(rc, internal_matches) =
LockedKeyBlobEntry::list(userDirName, [&](uid_t, const std::string&) {
// Need to filter on auth bound state, so just return true.
return true;
});
if (rc != ResponseCode::NO_ERROR) {
ALOGE("Error listing blob entries for user %d", userId);
return Status::fromServiceSpecificError(static_cast<int32_t>(rc));
}
auto it = uidsOut->begin();
for (LockedKeyBlobEntry& entry : internal_matches) {
if (it == uidsOut->end()) {
ALOGW("Maximum number (%d) of auth bound uids found, truncating remainder",
static_cast<int32_t>(uidsOut->capacity()));
break;
}
if (std::find(uidsOut->begin(), it, entry->uid()) != it) {
// uid already in list, skip
continue;
}
auto [rc, blob, charBlob] = entry.readBlobs(encryptionKey, state);
if (rc != ResponseCode::NO_ERROR && rc != ResponseCode::LOCKED) {
ALOGE("Error reading blob for key %s", entry->alias().c_str());
continue;
}
if (blob && blob.isEncrypted()) {
*it++ = entry->uid();
} else if (charBlob) {
auto [success, hwEnforced, swEnforced] = charBlob.getKeyCharacteristics();
if (!success) {
ALOGE("Error reading blob characteristics for key %s", entry->alias().c_str());
continue;
}
if (hwEnforced.Contains(TAG_USER_SECURE_ID) ||
swEnforced.Contains(TAG_USER_SECURE_ID)) {
*it++ = entry->uid();
}
}
}
*aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
return Status::ok();
}
Status KeyStoreService::reset(int32_t* aidl_return) {
if (!checkBinderPermission(P_RESET)) {
*aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);

View file

@ -61,6 +61,9 @@ class KeyStoreService : public android::security::keystore::BnKeystoreService {
int32_t* _aidl_return) override;
::android::binder::Status list(const ::android::String16& namePrefix, int32_t uid,
::std::vector<::android::String16>* _aidl_return) override;
::android::binder::Status listUidsOfAuthBoundKeys(::std::vector<int32_t>* uids,
int32_t* _aidl_return) override;
::android::binder::Status reset(int32_t* _aidl_return) override;
::android::binder::Status onUserPasswordChanged(int32_t userId,
const ::android::String16& newPassword,

View file

@ -66,6 +66,7 @@ void PrintUsageAndExit() {
" delete-all\n"
" exists --name=<key_name>\n"
" list [--prefix=<key_name_prefix>]\n"
" list-apps-with-keys\n"
" sign-verify --name=<key_name>\n"
" [en|de]crypt --name=<key_name> --in=<file> --out=<file>\n"
" [--seclevel=software|strongbox|tee(default)]\n"
@ -285,7 +286,8 @@ int AddEntropy(const std::string& input, int32_t flags) {
return result;
}
int GenerateKey(const std::string& name, int32_t flags) {
// Note: auth_bound keys created with this tool will not be usable.
int GenerateKey(const std::string& name, int32_t flags, bool auth_bound) {
std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
AuthorizationSetBuilder params;
params.RsaSigningKey(2048, 65537)
@ -294,8 +296,14 @@ int GenerateKey(const std::string& name, int32_t flags) {
.Digest(Digest::SHA_2_384)
.Digest(Digest::SHA_2_512)
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
.Padding(PaddingMode::RSA_PSS)
.Authorization(TAG_NO_AUTH_REQUIRED);
.Padding(PaddingMode::RSA_PSS);
if (auth_bound) {
// Gatekeeper normally generates the secure user id.
// Using zero allows the key to be created, but it will not be usuable.
params.Authorization(TAG_USER_SECURE_ID, 0);
} else {
params.Authorization(TAG_NO_AUTH_REQUIRED);
}
AuthorizationSet hardware_enforced_characteristics;
AuthorizationSet software_enforced_characteristics;
auto result = keystore->generateKey(name, params, flags, &hardware_enforced_characteristics,
@ -364,6 +372,35 @@ int List(const std::string& prefix) {
return 0;
}
int ListAppsWithKeys() {
sp<android::IServiceManager> sm = android::defaultServiceManager();
sp<android::IBinder> binder = sm->getService(String16("android.security.keystore"));
sp<IKeystoreService> service = android::interface_cast<IKeystoreService>(binder);
if (service == nullptr) {
fprintf(stderr, "Error connecting to keystore service.\n");
return 1;
}
int32_t aidl_return;
::std::vector<int32_t> uids(100);
android::binder::Status status = service->listUidsOfAuthBoundKeys(&uids, &aidl_return);
if (!status.isOk()) {
fprintf(stderr, "Requesting uids of auth bound keys failed with error %s.\n",
status.toString8().c_str());
return 1;
}
if (!KeyStoreNativeReturnCode(aidl_return).isOk()) {
fprintf(stderr, "Requesting uids of auth bound keys failed with code %d.\n", aidl_return);
return 1;
}
printf("Apps with auth bound keys:\n");
for (auto i = uids.begin(); i != uids.end(); ++i) {
if (*i == 0) break;
printf("%d\n", *i);
}
return 0;
}
int SignAndVerify(const std::string& name) {
std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
AuthorizationSetBuilder sign_params;
@ -591,7 +628,8 @@ int main(int argc, char** argv) {
securityLevelOption2Flags(*command_line));
} else if (args[0] == "generate") {
return GenerateKey(command_line->GetSwitchValueASCII("name"),
securityLevelOption2Flags(*command_line));
securityLevelOption2Flags(*command_line),
command_line->HasSwitch("auth_bound"));
} else if (args[0] == "get-chars") {
return GetCharacteristics(command_line->GetSwitchValueASCII("name"));
} else if (args[0] == "export") {
@ -604,6 +642,8 @@ int main(int argc, char** argv) {
return DoesKeyExist(command_line->GetSwitchValueASCII("name"));
} else if (args[0] == "list") {
return List(command_line->GetSwitchValueASCII("prefix"));
} else if (args[0] == "list-apps-with-keys") {
return ListAppsWithKeys();
} else if (args[0] == "sign-verify") {
return SignAndVerify(command_line->GetSwitchValueASCII("name"));
} else if (args[0] == "encrypt") {

View file

@ -0,0 +1,77 @@
#!/usr/bin/env bash
#
# Copyright (C) 2018 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.
#
#
# Simple adb based test for keystore method list_auth_bound_keys
# Depends on keystore_cli_v2 tool and root
#
set -e
ROOT_ID=0
USER1_ID=10901
USER2_ID=10902
SYSTEM_ID=1000
function cli {
adb shell "su $1 keystore_cli_v2 $2"
}
#start as root
adb root
# generate keys as user
echo "generating keys"
cli $USER1_ID "delete --name=no_auth_key" || true
cli $USER1_ID "generate --name=no_auth_key"
cli $USER2_ID "delete --name=auth_key" || true
if ! cli $USER2_ID "generate --name=auth_key --auth_bound"; then
echo "Unable to generate auth bound key, make sure device/emulator has a pin/password set."
echo "$ adb shell locksettings set-pin 1234"
exit 1
fi
# try to list keys as user
if cli $USER2_ID list-apps-with-keys; then
echo "Error: list-apps-with-keys succeeded as user, this is not expected!"
exit 1
fi
# try to list keys as root
if cli $ROOT_ID "list-apps-with-keys"; then
echo "Error: list-apps-with-keys succeeded as root, this is not expected!"
exit 1
fi
# try to list keys as system
success=false
while read -r line; do
echo $line
if [ "$line" == "$USER2_ID" ]; then
success=true
fi
if [ "$line" == "$USER1_ID" ]; then
echo "Error: User1 id not expected in list"
exit 1
fi
done <<< $(cli $SYSTEM_ID "list-apps-with-keys")
if [ $success = true ]; then
echo "Success!"
else
echo "Error: User2 id not in list"
exit 1
fi