Merge commit '320717adfab49735d0f5c330edda33a38540fcdb' into HEAD
Change-Id: I57d58be603c4706f8dbe50e2aa861b2c1456971f
This commit is contained in:
commit
f380c40827
9 changed files with 259 additions and 91 deletions
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "keystore"
|
||||
|
||||
#include "auth_token_table.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
@ -77,6 +79,12 @@ time_t clock_gettime_raw() {
|
|||
|
||||
void AuthTokenTable::AddAuthenticationToken(const HardwareAuthToken* auth_token) {
|
||||
Entry new_entry(auth_token, clock_function_());
|
||||
//STOPSHIP: debug only, to be removed
|
||||
ALOGD("AddAuthenticationToken: timestamp = %llu (%llu), time_received = %lld",
|
||||
static_cast<unsigned long long>(new_entry.timestamp_host_order()),
|
||||
static_cast<unsigned long long>(auth_token->timestamp),
|
||||
static_cast<long long>(new_entry.time_received()));
|
||||
|
||||
RemoveEntriesSupersededBy(new_entry);
|
||||
if (entries_.size() >= max_entries_) {
|
||||
ALOGW("Auth token table filled up; replacing oldest entry");
|
||||
|
@ -207,7 +215,7 @@ AuthTokenTable::Entry::Entry(const HardwareAuthToken* token, time_t current_time
|
|||
: token_(token), time_received_(current_time), last_use_(current_time),
|
||||
operation_completed_(token_->challenge == 0) {}
|
||||
|
||||
uint32_t AuthTokenTable::Entry::timestamp_host_order() const {
|
||||
uint64_t AuthTokenTable::Entry::timestamp_host_order() const {
|
||||
return ntoh(token_->timestamp);
|
||||
}
|
||||
|
||||
|
@ -235,7 +243,7 @@ bool AuthTokenTable::Entry::Supersedes(const Entry& entry) const {
|
|||
|
||||
return (token_->userId == entry.token_->userId &&
|
||||
token_->authenticatorType == entry.token_->authenticatorType &&
|
||||
token_->authenticatorType == entry.token_->authenticatorType &&
|
||||
token_->authenticatorId == entry.token_->authenticatorId &&
|
||||
timestamp_host_order() > entry.timestamp_host_order());
|
||||
}
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ class AuthTokenTable {
|
|||
const HardwareAuthToken* token() { return token_.get(); }
|
||||
time_t time_received() const { return time_received_; }
|
||||
bool completed() const { return operation_completed_; }
|
||||
uint32_t timestamp_host_order() const;
|
||||
uint64_t timestamp_host_order() const;
|
||||
HardwareAuthenticatorType authenticator_type() const;
|
||||
|
||||
private:
|
||||
|
|
|
@ -272,8 +272,9 @@ ResponseCode Blob::readBlob(const std::string& filename, const uint8_t* aes_key,
|
|||
return ResponseCode::VALUE_CORRUPTED;
|
||||
}
|
||||
|
||||
if ((isEncrypted() || isSuperEncrypted()) && (state != STATE_NO_ERROR)) {
|
||||
return ResponseCode::LOCKED;
|
||||
if ((isEncrypted() || isSuperEncrypted())) {
|
||||
if (state == STATE_LOCKED) return ResponseCode::LOCKED;
|
||||
if (state == STATE_UNINITIALIZED) return ResponseCode::UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (fileLength < offsetof(blobv3, value)) return ResponseCode::VALUE_CORRUPTED;
|
||||
|
|
|
@ -25,8 +25,10 @@ static constexpr uint64_t kInvalidGrantNo = std::numeric_limits<uint64_t>::max()
|
|||
static const char* kKeystoreGrantInfix = "_KEYSTOREGRANT_";
|
||||
static constexpr size_t kKeystoreGrantInfixLength = 15;
|
||||
|
||||
Grant::Grant(const std::string& alias, const std::string& key_file, const uint64_t grant_no)
|
||||
: alias_(alias), key_file_(key_file), grant_no_(grant_no) {}
|
||||
Grant::Grant(const std::string& alias, const std::string& owner_dir_name, const uid_t owner_uid,
|
||||
const uint64_t grant_no)
|
||||
: alias_(alias), owner_dir_name_(owner_dir_name), owner_uid_(owner_uid),
|
||||
grant_no_(grant_no) {}
|
||||
|
||||
static std::pair<uint64_t, std::string> parseGrantAlias(const std::string& grantAlias) {
|
||||
auto pos = grantAlias.rfind(kKeystoreGrantInfix);
|
||||
|
@ -39,7 +41,8 @@ static std::pair<uint64_t, std::string> parseGrantAlias(const std::string& grant
|
|||
return {grant_no, wrapped_alias};
|
||||
}
|
||||
|
||||
std::string GrantStore::put(const uid_t uid, const std::string& alias, const std::string& key_file) {
|
||||
std::string GrantStore::put(const uid_t uid, const std::string& alias,
|
||||
const std::string& owner_dir_name, const uid_t owner_uid) {
|
||||
std::stringstream s;
|
||||
s << alias << kKeystoreGrantInfix;
|
||||
auto& uid_grant_list = grants_[uid];
|
||||
|
@ -47,10 +50,12 @@ std::string GrantStore::put(const uid_t uid, const std::string& alias, const std
|
|||
bool success = false;
|
||||
auto iterator = std::find_if(uid_grant_list.begin(), uid_grant_list.end(),
|
||||
[&](auto& entry) {
|
||||
return success = entry.alias_ == alias && entry.key_file_ == key_file;
|
||||
return success = entry.alias_ == alias && entry.owner_dir_name_ == owner_dir_name
|
||||
&& entry.owner_uid_ == owner_uid;
|
||||
});
|
||||
while (!success) {
|
||||
std::tie(iterator, success) = uid_grant_list.emplace(alias, key_file, std::rand());
|
||||
std::tie(iterator, success) = uid_grant_list.emplace(alias, owner_dir_name, owner_uid,
|
||||
std::rand());
|
||||
}
|
||||
s << iterator->grant_no_;
|
||||
return s.str();
|
||||
|
@ -70,10 +75,11 @@ const Grant* GrantStore::get(const uid_t uid, const std::string& alias) const {
|
|||
return &(*grant);
|
||||
}
|
||||
|
||||
bool GrantStore::removeByFileName(const uid_t uid, const std::string& fileName) {
|
||||
auto& uid_grant_list = grants_.operator[](uid);
|
||||
bool GrantStore::removeByFileAlias(const uid_t granteeUid, const uid_t granterUid,
|
||||
const std::string& alias) {
|
||||
auto& uid_grant_list = grants_[granteeUid];
|
||||
for (auto i = uid_grant_list.begin(); i != uid_grant_list.end(); ++i) {
|
||||
if (i->key_file_ == fileName) {
|
||||
if (i->alias_ == alias && i->owner_uid_ == granterUid) {
|
||||
uid_grant_list.erase(i);
|
||||
return true;
|
||||
}
|
||||
|
@ -81,4 +87,19 @@ bool GrantStore::removeByFileName(const uid_t uid, const std::string& fileName)
|
|||
return false;
|
||||
}
|
||||
|
||||
void GrantStore::removeAllGrantsToKey(const uid_t granterUid, const std::string& alias) {
|
||||
for (auto& uid_grant_list : grants_) {
|
||||
for (auto i = uid_grant_list.second.begin(); i != uid_grant_list.second.end(); ++i) {
|
||||
if (i->alias_ == alias && i->owner_uid_ == granterUid) {
|
||||
uid_grant_list.second.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GrantStore::removeAllGrantsToUid(const uid_t granteeUid) {
|
||||
grants_.erase(granteeUid);
|
||||
}
|
||||
|
||||
} // namespace keystore
|
||||
|
|
|
@ -32,10 +32,14 @@ namespace keystore {
|
|||
*/
|
||||
class Grant {
|
||||
public:
|
||||
Grant(const std::string& alias, const std::string& key_file, const uint64_t grant_no);
|
||||
std::string alias_;
|
||||
std::string key_file_;
|
||||
uint64_t grant_no_;
|
||||
Grant(const std::string& alias, const std::string& owner_dir_name, const uid_t owner_uid,
|
||||
const uint64_t grant_no);
|
||||
// the following three field are used to recover the key filename that the grant refers to
|
||||
std::string alias_; ///< original/wrapped key alias
|
||||
std::string owner_dir_name_; ///< key owner key directory
|
||||
uid_t owner_uid_; ///< key owner uid
|
||||
|
||||
uint64_t grant_no_; ///< numeric grant identifier - randomly assigned
|
||||
|
||||
operator const uint64_t&() const { return grant_no_; }
|
||||
};
|
||||
|
@ -52,9 +56,12 @@ public:
|
|||
class GrantStore {
|
||||
public:
|
||||
GrantStore() : grants_() {}
|
||||
std::string put(const uid_t uid, const std::string& alias, const std::string& key_file);
|
||||
std::string put(const uid_t uid, const std::string& alias, const std::string& owner_dir_name,
|
||||
const uid_t owner_uid);
|
||||
const Grant* get(const uid_t uid, const std::string& alias) const;
|
||||
bool removeByFileName(const uid_t uid, const std::string& filename);
|
||||
bool removeByFileAlias(const uid_t granteeUid, const uid_t granterUid, const std::string& alias);
|
||||
void removeAllGrantsToKey(const uid_t granterUid, const std::string& alias);
|
||||
void removeAllGrantsToUid(const uid_t granteeUid);
|
||||
|
||||
// GrantStore is neither copyable nor movable.
|
||||
GrantStore(const GrantStore&) = delete;
|
||||
|
|
|
@ -191,16 +191,21 @@ KeyStoreServiceReturnCode KeyStoreService::del(const String16& name, int targetU
|
|||
}
|
||||
String8 name8(name);
|
||||
ALOGI("del %s %d", name8.string(), targetUid);
|
||||
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid, ::TYPE_ANY));
|
||||
ResponseCode result = mKeyStore->del(filename.string(), ::TYPE_ANY, get_user_id(targetUid));
|
||||
auto filename = mKeyStore->getBlobFileNameIfExists(name8, targetUid, ::TYPE_ANY);
|
||||
if (!filename.isOk()) return ResponseCode::KEY_NOT_FOUND;
|
||||
|
||||
ResponseCode result = mKeyStore->del(filename.value().string(), ::TYPE_ANY,
|
||||
get_user_id(targetUid));
|
||||
if (result != ResponseCode::NO_ERROR) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Also delete any characteristics files
|
||||
String8 chrFilename(
|
||||
mKeyStore->getKeyNameForUidWithDir(name8, targetUid, ::TYPE_KEY_CHARACTERISTICS));
|
||||
return mKeyStore->del(chrFilename.string(), ::TYPE_KEY_CHARACTERISTICS, get_user_id(targetUid));
|
||||
filename = mKeyStore->getBlobFileNameIfExists(name8, targetUid, ::TYPE_KEY_CHARACTERISTICS);
|
||||
if (filename.isOk()) {
|
||||
return mKeyStore->del(filename.value().string(), ::TYPE_KEY_CHARACTERISTICS,
|
||||
get_user_id(targetUid));
|
||||
}
|
||||
return ResponseCode::NO_ERROR;
|
||||
}
|
||||
|
||||
KeyStoreServiceReturnCode KeyStoreService::exist(const String16& name, int targetUid) {
|
||||
|
@ -209,13 +214,8 @@ KeyStoreServiceReturnCode KeyStoreService::exist(const String16& name, int targe
|
|||
return ResponseCode::PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
String8 name8(name);
|
||||
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid, ::TYPE_ANY));
|
||||
|
||||
if (access(filename.string(), R_OK) == -1) {
|
||||
return (errno != ENOENT) ? ResponseCode::SYSTEM_ERROR : ResponseCode::KEY_NOT_FOUND;
|
||||
}
|
||||
return ResponseCode::NO_ERROR;
|
||||
auto filename = mKeyStore->getBlobFileNameIfExists(String8(name), targetUid, ::TYPE_ANY);
|
||||
return filename.isOk() ? ResponseCode::NO_ERROR : ResponseCode::KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
KeyStoreServiceReturnCode KeyStoreService::list(const String16& prefix, int targetUid,
|
||||
|
@ -526,7 +526,7 @@ String16 KeyStoreService::grant(const String16& name, int32_t granteeUid) {
|
|||
return String16();
|
||||
}
|
||||
|
||||
return String16(mKeyStore->addGrant(filename.string(), String8(name).string(), granteeUid).c_str());
|
||||
return String16(mKeyStore->addGrant(String8(name).string(), callingUid, granteeUid).c_str());
|
||||
}
|
||||
|
||||
KeyStoreServiceReturnCode KeyStoreService::ungrant(const String16& name, int32_t granteeUid) {
|
||||
|
@ -543,8 +543,8 @@ KeyStoreServiceReturnCode KeyStoreService::ungrant(const String16& name, int32_t
|
|||
return (errno != ENOENT) ? ResponseCode::SYSTEM_ERROR : ResponseCode::KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
return mKeyStore->removeGrant(filename.string(), granteeUid) ? ResponseCode::NO_ERROR
|
||||
: ResponseCode::KEY_NOT_FOUND;
|
||||
return mKeyStore->removeGrant(name8, callingUid, granteeUid) ? ResponseCode::NO_ERROR
|
||||
: ResponseCode::KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
int64_t KeyStoreService::getmtime(const String16& name, int32_t uid) {
|
||||
|
@ -554,17 +554,16 @@ int64_t KeyStoreService::getmtime(const String16& name, int32_t uid) {
|
|||
return -1L;
|
||||
}
|
||||
|
||||
String8 name8(name);
|
||||
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, targetUid, ::TYPE_ANY));
|
||||
auto filename = mKeyStore->getBlobFileNameIfExists(String8(name), targetUid, ::TYPE_ANY);
|
||||
|
||||
if (access(filename.string(), R_OK) == -1) {
|
||||
ALOGW("could not access %s for getmtime", filename.string());
|
||||
if (!filename.isOk()) {
|
||||
ALOGW("could not access %s for getmtime", filename.value().string());
|
||||
return -1L;
|
||||
}
|
||||
|
||||
int fd = TEMP_FAILURE_RETRY(open(filename.string(), O_NOFOLLOW, O_RDONLY));
|
||||
int fd = TEMP_FAILURE_RETRY(open(filename.value().string(), O_NOFOLLOW, O_RDONLY));
|
||||
if (fd < 0) {
|
||||
ALOGW("could not open %s for getmtime", filename.string());
|
||||
ALOGW("could not open %s for getmtime", filename.value().string());
|
||||
return -1L;
|
||||
}
|
||||
|
||||
|
@ -572,7 +571,7 @@ int64_t KeyStoreService::getmtime(const String16& name, int32_t uid) {
|
|||
int ret = fstat(fd, &s);
|
||||
close(fd);
|
||||
if (ret == -1) {
|
||||
ALOGW("could not stat %s for getmtime", filename.string());
|
||||
ALOGW("could not stat %s for getmtime", filename.value().string());
|
||||
return -1L;
|
||||
}
|
||||
|
||||
|
@ -652,6 +651,8 @@ KeyStoreServiceReturnCode KeyStoreService::clear_uid(int64_t targetUid64) {
|
|||
}
|
||||
ALOGI("clear_uid %" PRId64, targetUid64);
|
||||
|
||||
mKeyStore->removeAllGrantsToUid(targetUid);
|
||||
|
||||
String8 prefix = String8::format("%u_", targetUid);
|
||||
Vector<String16> aliases;
|
||||
if (mKeyStore->list(prefix, &aliases, get_user_id(targetUid)) != ResponseCode::NO_ERROR) {
|
||||
|
@ -800,7 +801,26 @@ KeyStoreService::getKeyCharacteristics(const String16& name, const hidl_vec<uint
|
|||
|
||||
KeyStoreServiceReturnCode rc =
|
||||
mKeyStore->getKeyForName(&keyBlob, name8, targetUid, TYPE_KEYMASTER_10);
|
||||
if (!rc.isOk()) {
|
||||
if (rc == ResponseCode::UNINITIALIZED) {
|
||||
/*
|
||||
* If we fail reading the blob because the master key is missing we try to retrieve the
|
||||
* key characteristics from the characteristics file. This happens when auth-bound
|
||||
* keys are used after a screen lock has been removed by the user.
|
||||
*/
|
||||
rc = mKeyStore->getKeyForName(&keyBlob, name8, targetUid, TYPE_KEY_CHARACTERISTICS);
|
||||
if (!rc.isOk()) {
|
||||
return rc;
|
||||
}
|
||||
AuthorizationSet keyCharacteristics;
|
||||
// TODO write one shot stream buffer to avoid copying (twice here)
|
||||
std::string charBuffer(reinterpret_cast<const char*>(keyBlob.getValue()),
|
||||
keyBlob.getLength());
|
||||
std::stringstream charStream(charBuffer);
|
||||
keyCharacteristics.Deserialize(&charStream);
|
||||
|
||||
outCharacteristics->softwareEnforced = keyCharacteristics.hidl_data();
|
||||
return rc;
|
||||
} else if (!rc.isOk()) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1073,12 +1093,15 @@ void KeyStoreService::begin(const sp<IBinder>& appToken, const String16& name, K
|
|||
persistedCharacteristics.Subtract(teeEnforced);
|
||||
characteristics.softwareEnforced = persistedCharacteristics.hidl_data();
|
||||
|
||||
result->resultCode = getAuthToken(characteristics, 0, purpose, &authToken,
|
||||
/*failOnTokenMissing*/ false);
|
||||
auto authResult = getAuthToken(characteristics, 0, purpose, &authToken,
|
||||
/*failOnTokenMissing*/ false);
|
||||
// If per-operation auth is needed we need to begin the operation and
|
||||
// the client will need to authorize that operation before calling
|
||||
// update. Any other auth issues stop here.
|
||||
if (!result->resultCode.isOk() && result->resultCode != ResponseCode::OP_AUTH_NEEDED) return;
|
||||
if (!authResult.isOk() && authResult != ResponseCode::OP_AUTH_NEEDED) {
|
||||
result->resultCode = authResult;
|
||||
return;
|
||||
}
|
||||
|
||||
addAuthTokenToParams(&opParams, authToken);
|
||||
|
||||
|
@ -1153,6 +1176,7 @@ void KeyStoreService::begin(const sp<IBinder>& appToken, const String16& name, K
|
|||
result->handle, keyid, purpose, dev, appToken, std::move(characteristics), pruneable);
|
||||
assert(characteristics.teeEnforced.size() == 0);
|
||||
assert(characteristics.softwareEnforced.size() == 0);
|
||||
result->token = operationToken;
|
||||
|
||||
if (authToken) {
|
||||
mOperationMap.setOperationAuthToken(operationToken, authToken);
|
||||
|
@ -1162,8 +1186,11 @@ void KeyStoreService::begin(const sp<IBinder>& appToken, const String16& name, K
|
|||
// application should get an auth token using the handle before the
|
||||
// first call to update, which will fail if keystore hasn't received the
|
||||
// auth token.
|
||||
// All fields but "token" were set in the begin operation's callback.
|
||||
result->token = operationToken;
|
||||
if (result->resultCode == ErrorCode::OK) {
|
||||
result->resultCode = authResult;
|
||||
}
|
||||
|
||||
// Other result fields were set in the begin operation's callback.
|
||||
}
|
||||
|
||||
void KeyStoreService::update(const sp<IBinder>& token, const hidl_vec<KeyParameter>& params,
|
||||
|
@ -1690,6 +1717,7 @@ KeyStoreServiceReturnCode KeyStoreService::getAuthToken(const KeyCharacteristics
|
|||
case AuthTokenTable::AUTH_TOKEN_NOT_FOUND:
|
||||
case AuthTokenTable::AUTH_TOKEN_EXPIRED:
|
||||
case AuthTokenTable::AUTH_TOKEN_WRONG_SID:
|
||||
ALOGE("getAuthToken failed: %d", err); //STOPSHIP: debug only, to be removed
|
||||
return ErrorCode::KEY_USER_NOT_AUTHENTICATED;
|
||||
case AuthTokenTable::OP_HANDLE_REQUIRED:
|
||||
return failOnTokenMissing ? KeyStoreServiceReturnCode(ErrorCode::KEY_USER_NOT_AUTHENTICATED)
|
||||
|
@ -1867,15 +1895,12 @@ KeyStoreServiceReturnCode KeyStoreService::upgradeKeyBlob(const String16& name,
|
|||
return;
|
||||
}
|
||||
|
||||
String8 filename(mKeyStore->getKeyNameForUidWithDir(name8, uid, ::TYPE_KEYMASTER_10));
|
||||
error = mKeyStore->del(filename.string(), ::TYPE_ANY, get_user_id(uid));
|
||||
if(error == ResponseCode::KEY_NOT_FOUND){
|
||||
uid_t euid = get_keystore_euid(uid);
|
||||
if ((euid != uid) && (euid == AID_WIFI)) {
|
||||
filename=mKeyStore->getKeyNameForUidWithDir(name8, euid, ::TYPE_KEYMASTER_10);
|
||||
error=mKeyStore->del(filename.string(), ::TYPE_ANY, get_user_id(euid));
|
||||
}
|
||||
auto filename = mKeyStore->getBlobFileNameIfExists(name8, uid, ::TYPE_KEYMASTER_10);
|
||||
if (!filename.isOk()) {
|
||||
ALOGI("trying to upgrade a non existing blob");
|
||||
return;
|
||||
}
|
||||
error = mKeyStore->del(filename.value().string(), ::TYPE_ANY, get_user_id(uid));
|
||||
if (!error.isOk()) {
|
||||
ALOGI("upgradeKeyBlob keystore->del failed %d", (int)error);
|
||||
return;
|
||||
|
@ -1888,7 +1913,7 @@ KeyStoreServiceReturnCode KeyStoreService::upgradeKeyBlob(const String16& name,
|
|||
newBlob.setSuperEncrypted(blob->isSuperEncrypted());
|
||||
newBlob.setCriticalToDeviceEncryption(blob->isCriticalToDeviceEncryption());
|
||||
|
||||
error = mKeyStore->put(filename.string(), &newBlob, get_user_id(uid));
|
||||
error = mKeyStore->put(filename.value().string(), &newBlob, get_user_id(uid));
|
||||
if (!error.isOk()) {
|
||||
ALOGI("upgradeKeyBlob keystore->put failed %d", (int)error);
|
||||
return;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <openssl/bio.h>
|
||||
|
||||
#include <utils/String16.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
#include <keystore/IKeystoreService.h>
|
||||
|
||||
|
@ -39,6 +40,7 @@ const char* KeyStore::sMetaDataFile = ".metadata";
|
|||
const android::String16 KeyStore::sRSAKeyType("RSA");
|
||||
|
||||
using namespace keystore;
|
||||
using android::String8;
|
||||
|
||||
KeyStore::KeyStore(Entropy* entropy, const km_device_t& device, const km_device_t& fallback,
|
||||
bool allowNewFallback)
|
||||
|
@ -154,6 +156,30 @@ android::String8 KeyStore::getKeyNameForUidWithDir(
|
|||
}
|
||||
}
|
||||
|
||||
NullOr<android::String8> KeyStore::getBlobFileNameIfExists(const android::String8& alias, uid_t uid,
|
||||
const BlobType type) {
|
||||
android::String8 filepath8(getKeyNameForUidWithDir(alias, uid, type));
|
||||
|
||||
if (!access(filepath8.string(), R_OK | W_OK)) return filepath8;
|
||||
|
||||
// If this is one of the legacy UID->UID mappings, use it.
|
||||
uid_t euid = get_keystore_euid(uid);
|
||||
if (euid != uid) {
|
||||
filepath8 = getKeyNameForUidWithDir(alias, euid, type);
|
||||
if (!access(filepath8.string(), R_OK | W_OK)) return filepath8;
|
||||
}
|
||||
|
||||
// They might be using a granted key.
|
||||
auto grant = mGrants.get(uid, alias.string());
|
||||
if (grant) {
|
||||
filepath8 = String8::format("%s/%s", grant->owner_dir_name_.c_str(),
|
||||
getKeyNameForUid(String8(grant->alias_.c_str()), grant->owner_uid_, type).c_str());
|
||||
if (!access(filepath8.string(), R_OK | W_OK)) return filepath8;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
void KeyStore::resetUser(uid_t userId, bool keepUnenryptedEntries) {
|
||||
android::String8 prefix("");
|
||||
android::Vector<android::String16> aliases;
|
||||
|
@ -308,11 +334,23 @@ ResponseCode KeyStore::put(const char* filename, Blob* keyBlob, uid_t userId) {
|
|||
mEntropy);
|
||||
}
|
||||
|
||||
static NullOr<std::tuple<uid_t, std::string>> filename2UidAlias(const std::string& filename);
|
||||
|
||||
ResponseCode KeyStore::del(const char* filename, const BlobType type, uid_t userId) {
|
||||
Blob keyBlob;
|
||||
auto uidAlias = filename2UidAlias(filename);
|
||||
uid_t uid;
|
||||
std::string alias;
|
||||
if (uidAlias.isOk()) {
|
||||
std::tie(uid, alias) = std::move(uidAlias).value();
|
||||
}
|
||||
ResponseCode rc = get(filename, &keyBlob, type, userId);
|
||||
if (rc == ResponseCode::VALUE_CORRUPTED) {
|
||||
// The file is corrupt, the best we can do is rm it.
|
||||
if (uidAlias.isOk()) {
|
||||
// remove possible grants
|
||||
mGrants.removeAllGrantsToKey(uid, alias);
|
||||
}
|
||||
return (unlink(filename) && errno != ENOENT) ?
|
||||
ResponseCode::SYSTEM_ERROR : ResponseCode::NO_ERROR;
|
||||
}
|
||||
|
@ -330,8 +368,16 @@ ResponseCode KeyStore::del(const char* filename, const BlobType type, uid_t user
|
|||
return ResponseCode::SYSTEM_ERROR;
|
||||
}
|
||||
|
||||
return (unlink(filename) && errno != ENOENT) ?
|
||||
rc = (unlink(filename) && errno != ENOENT) ?
|
||||
ResponseCode::SYSTEM_ERROR : ResponseCode::NO_ERROR;
|
||||
|
||||
if (rc == ResponseCode::NO_ERROR && keyBlob.getType() != ::TYPE_KEY_CHARACTERISTICS) {
|
||||
// now that we have successfully deleted a key, let's make sure there are no stale grants
|
||||
if (uidAlias.isOk()) {
|
||||
mGrants.removeAllGrantsToKey(uid, alias);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -371,6 +417,29 @@ static void decode_key(char* out, const char* in, size_t length) {
|
|||
*out = '\0';
|
||||
}
|
||||
|
||||
static NullOr<std::tuple<uid_t, std::string>> filename2UidAlias(const std::string& filepath) {
|
||||
auto filenamebase = filepath.find_last_of('/');
|
||||
std::string filename = filenamebase == std::string::npos ? filepath :
|
||||
filepath.substr(filenamebase + 1);
|
||||
|
||||
if (filename[0] == '.') return {};
|
||||
|
||||
auto sep = filename.find('_');
|
||||
if (sep == std::string::npos) return {};
|
||||
|
||||
std::stringstream s(filename.substr(0, sep));
|
||||
uid_t uid;
|
||||
s >> uid;
|
||||
if (!s) return {};
|
||||
|
||||
auto alias = filename.substr(sep + 1);
|
||||
|
||||
std::vector<char> alias_buffer(decode_key_length(alias.c_str(), alias.size()) + 1);
|
||||
|
||||
decode_key(alias_buffer.data(), alias.c_str(), alias.size());
|
||||
return std::tuple<uid_t, std::string>(uid, alias_buffer.data());
|
||||
}
|
||||
|
||||
ResponseCode KeyStore::list(const android::String8& prefix,
|
||||
android::Vector<android::String16>* matches, uid_t userId) {
|
||||
|
||||
|
@ -414,12 +483,16 @@ ResponseCode KeyStore::list(const android::String8& prefix,
|
|||
return ResponseCode::NO_ERROR;
|
||||
}
|
||||
|
||||
std::string KeyStore::addGrant(const char* filename, const char* alias, uid_t granteeUid) {
|
||||
return mGrants.put(granteeUid, alias, filename);
|
||||
std::string KeyStore::addGrant(const char* alias, uid_t granterUid, uid_t granteeUid) {
|
||||
return mGrants.put(granteeUid, alias, getUserStateByUid(granterUid)->getUserDirName(),
|
||||
granterUid);
|
||||
}
|
||||
|
||||
bool KeyStore::removeGrant(const char* filename, uid_t granteeUid) {
|
||||
return mGrants.removeByFileName(granteeUid, filename);
|
||||
bool KeyStore::removeGrant(const char* alias, const uid_t granterUid, const uid_t granteeUid) {
|
||||
return mGrants.removeByFileAlias(granteeUid, granterUid, alias);
|
||||
}
|
||||
void KeyStore::removeAllGrantsToUid(const uid_t granteeUid) {
|
||||
mGrants.removeAllGrantsToUid(granteeUid);
|
||||
}
|
||||
|
||||
ResponseCode KeyStore::importKey(const uint8_t* key, size_t keyLen, const char* filename,
|
||||
|
@ -498,31 +571,13 @@ bool KeyStore::isHardwareBacked(const android::String16& /*keyType*/) const {
|
|||
|
||||
ResponseCode KeyStore::getKeyForName(Blob* keyBlob, const android::String8& keyName,
|
||||
const uid_t uid, const BlobType type) {
|
||||
android::String8 filepath8(getKeyNameForUidWithDir(keyName, uid, type));
|
||||
auto filepath8 = getBlobFileNameIfExists(keyName, uid, type);
|
||||
uid_t userId = get_user_id(uid);
|
||||
|
||||
ResponseCode responseCode = get(filepath8.string(), keyBlob, type, userId);
|
||||
if (responseCode == ResponseCode::NO_ERROR) {
|
||||
return responseCode;
|
||||
}
|
||||
if (filepath8.isOk())
|
||||
return get(filepath8.value().string(), keyBlob, type, userId);
|
||||
|
||||
// If this is one of the legacy UID->UID mappings, use it.
|
||||
uid_t euid = get_keystore_euid(uid);
|
||||
if (euid != uid) {
|
||||
filepath8 = getKeyNameForUidWithDir(keyName, euid, type);
|
||||
responseCode = get(filepath8.string(), keyBlob, type, userId);
|
||||
if (responseCode == ResponseCode::NO_ERROR) {
|
||||
return responseCode;
|
||||
}
|
||||
}
|
||||
|
||||
// They might be using a granted key.
|
||||
auto grant = mGrants.get(uid, keyName.string());
|
||||
if (!grant) return ResponseCode::KEY_NOT_FOUND;
|
||||
filepath8 = grant->key_file_.c_str();
|
||||
|
||||
// It is a granted key. Try to load it.
|
||||
return get(filepath8.string(), keyBlob, type, userId);
|
||||
return ResponseCode::KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
UserState* KeyStore::getUserState(uid_t userId) {
|
||||
|
@ -569,7 +624,7 @@ const UserState* KeyStore::getUserStateByUid(uid_t uid) const {
|
|||
}
|
||||
|
||||
bool KeyStore::upgradeBlob(const char* filename, Blob* blob, const uint8_t oldVersion,
|
||||
const BlobType type, uid_t uid) {
|
||||
const BlobType type, uid_t userId) {
|
||||
bool updated = false;
|
||||
uint8_t version = oldVersion;
|
||||
|
||||
|
@ -579,7 +634,7 @@ bool KeyStore::upgradeBlob(const char* filename, Blob* blob, const uint8_t oldVe
|
|||
|
||||
blob->setType(type);
|
||||
if (type == TYPE_KEY_PAIR) {
|
||||
importBlobAsKey(blob, filename, uid);
|
||||
importBlobAsKey(blob, filename, userId);
|
||||
}
|
||||
version = 1;
|
||||
updated = true;
|
||||
|
@ -611,7 +666,7 @@ struct BIO_Delete {
|
|||
};
|
||||
typedef std::unique_ptr<BIO, BIO_Delete> Unique_BIO;
|
||||
|
||||
ResponseCode KeyStore::importBlobAsKey(Blob* blob, const char* filename, uid_t uid) {
|
||||
ResponseCode KeyStore::importBlobAsKey(Blob* blob, const char* filename, uid_t userId) {
|
||||
// We won't even write to the blob directly with this BIO, so const_cast is okay.
|
||||
Unique_BIO b(BIO_new_mem_buf(const_cast<uint8_t*>(blob->getValue()), blob->getLength()));
|
||||
if (b.get() == NULL) {
|
||||
|
@ -639,13 +694,13 @@ ResponseCode KeyStore::importBlobAsKey(Blob* blob, const char* filename, uid_t u
|
|||
return ResponseCode::SYSTEM_ERROR;
|
||||
}
|
||||
|
||||
ResponseCode rc = importKey(pkcs8key.get(), len, filename, get_user_id(uid),
|
||||
ResponseCode rc = importKey(pkcs8key.get(), len, filename, userId,
|
||||
blob->isEncrypted() ? KEYSTORE_FLAG_ENCRYPTED : KEYSTORE_FLAG_NONE);
|
||||
if (rc != ResponseCode::NO_ERROR) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return get(filename, blob, TYPE_KEY_PAIR, uid);
|
||||
return get(filename, blob, TYPE_KEY_PAIR, userId);
|
||||
}
|
||||
|
||||
void KeyStore::readMetaData() {
|
||||
|
|
|
@ -70,6 +70,8 @@ class KeyStore {
|
|||
const BlobType type);
|
||||
android::String8 getKeyNameForUidWithDir(const android::String8& keyName, uid_t uid,
|
||||
const BlobType type);
|
||||
NullOr<android::String8> getBlobFileNameIfExists(const android::String8& alias, uid_t uid,
|
||||
const BlobType type);
|
||||
|
||||
/*
|
||||
* Delete entries owned by userId. If keepUnencryptedEntries is true
|
||||
|
@ -87,8 +89,9 @@ class KeyStore {
|
|||
ResponseCode list(const android::String8& prefix, android::Vector<android::String16>* matches,
|
||||
uid_t userId);
|
||||
|
||||
std::string addGrant(const char* filename, const char* alias, uid_t granteeUid);
|
||||
bool removeGrant(const char* filename, uid_t granteeUid);
|
||||
std::string addGrant(const char* alias, uid_t granterUid, uid_t granteeUid);
|
||||
bool removeGrant(const char* alias, const uid_t granterUid, const uid_t granteeUid);
|
||||
void removeAllGrantsToUid(const uid_t granteeUid);
|
||||
|
||||
ResponseCode importKey(const uint8_t* key, size_t keyLen, const char* filename, uid_t userId,
|
||||
int32_t flags);
|
||||
|
|
|
@ -61,4 +61,52 @@ bool is_granted_to(uid_t callingUid, uid_t targetUid);
|
|||
|
||||
int configure_selinux();
|
||||
|
||||
/*
|
||||
* Keystore grants.
|
||||
*
|
||||
* What are keystore grants?
|
||||
*
|
||||
* Keystore grants are a mechanism that allows an app to grant the permission to use one of its
|
||||
* keys to an other app.
|
||||
*
|
||||
* Liftime of a grant:
|
||||
*
|
||||
* A keystore grant is ephemeral in that is never persistently stored. When the keystore process
|
||||
* exits, all grants are lost. Also, grants can be explicitly revoked by the granter by invoking
|
||||
* the ungrant operation.
|
||||
*
|
||||
* What happens when a grant is created?
|
||||
*
|
||||
* The grant operation expects a valid key alias and the uid of the grantee, i.e., the app that
|
||||
* shall be allowed to use the key denoted by the alias. It then makes an entry in the grant store
|
||||
* which generates a new alias of the form <alias>_KEYSTOREGRANT_<random_grant_no_>. This grant
|
||||
* alias is returned to the caller which can pass the new alias to the grantee. For every grantee,
|
||||
* the grant store keeps a set of grants, an entry of which holds the following information:
|
||||
* - the owner of the key by uid, aka granter uid,
|
||||
* - the original alias of the granted key, and
|
||||
* - the random grant number.
|
||||
* (See "grant_store.h:class Grant")
|
||||
*
|
||||
* What happens when a grant is used?
|
||||
*
|
||||
* Upon any keystore operation that expects an alias, the alias and the caller's uid are used
|
||||
* to retrieve a key file. If that fails some operations try to retrieve a key file indirectly
|
||||
* through a grant. These operations include:
|
||||
* - attestKey
|
||||
* - begin
|
||||
* - exportKey
|
||||
* - get
|
||||
* - getKeyCharacteristics
|
||||
* - del
|
||||
* - exist
|
||||
* - getmtime
|
||||
* Operations that DO NOT follow the grant indirection are:
|
||||
* - import
|
||||
* - generate
|
||||
* - grant
|
||||
* - ungrant
|
||||
* Especially, the latter two mean that neither can a grantee transitively grant a granted key
|
||||
* to a third, nor can they relinquish access to the key or revoke access to the key by a third.
|
||||
*/
|
||||
|
||||
#endif // KEYSTORE_PERMISSIONS_H_
|
||||
|
|
Loading…
Reference in a new issue