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:
parent
adde739791
commit
eb7f79b832
5 changed files with 198 additions and 4 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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") {
|
||||
|
|
77
keystore/tests/list_auth_bound_keys_test.sh
Executable file
77
keystore/tests/list_auth_bound_keys_test.sh
Executable 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
|
Loading…
Reference in a new issue