diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2017-10-09 19:01:45 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2017-10-09 19:01:45 +0000 |
commit | 07c1cfe055173569c68db397015ccc3f97b3aff0 (patch) | |
tree | b4d511d7948d080b319783bdd762214823d46966 | |
parent | 2541243b9f984fb7f9473cd32e23f5fae2b48034 (diff) | |
parent | 870663590002d6714db72360329e4d12bf90ffaf (diff) | |
download | security-07c1cfe055173569c68db397015ccc3f97b3aff0.tar.gz |
Snap for 4384531 from 870663590002d6714db72360329e4d12bf90ffaf to oc-m2-release
Change-Id: Ie44da762c2a496e98e773359ca0fc7a61f1cce15
-rw-r--r-- | keystore/grant_store.cpp | 22 | ||||
-rw-r--r-- | keystore/grant_store.h | 14 | ||||
-rw-r--r-- | keystore/key_store_service.cpp | 55 | ||||
-rw-r--r-- | keystore/keystore.cpp | 113 | ||||
-rw-r--r-- | keystore/keystore.h | 5 | ||||
-rw-r--r-- | keystore/permissions.h | 48 |
6 files changed, 192 insertions, 65 deletions
diff --git a/keystore/grant_store.cpp b/keystore/grant_store.cpp index 2fb09c17..9244b7c4 100644 --- a/keystore/grant_store.cpp +++ b/keystore/grant_store.cpp @@ -75,10 +75,11 @@ const Grant* GrantStore::get(const uid_t uid, const std::string& alias) const { return &(*grant); } -bool GrantStore::removeByFileAlias(const uid_t uid, const std::string& alias) { - auto& uid_grant_list = grants_[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->alias_ == alias) { + if (i->alias_ == alias && i->owner_uid_ == granterUid) { uid_grant_list.erase(i); return true; } @@ -86,4 +87,19 @@ bool GrantStore::removeByFileAlias(const uid_t uid, const std::string& alias) { 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 diff --git a/keystore/grant_store.h b/keystore/grant_store.h index ab03630e..6341c767 100644 --- a/keystore/grant_store.h +++ b/keystore/grant_store.h @@ -34,10 +34,12 @@ class Grant { public: Grant(const std::string& alias, const std::string& owner_dir_name, const uid_t owner_uid, const uint64_t grant_no); - std::string alias_; - std::string owner_dir_name_; - uid_t owner_uid_; - 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_; } }; @@ -57,7 +59,9 @@ public: 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 removeByFileAlias(const uid_t uid, const std::string& alias); + 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; diff --git a/keystore/key_store_service.cpp b/keystore/key_store_service.cpp index 310d8e2d..85de1813 100644 --- a/keystore/key_store_service.cpp +++ b/keystore/key_store_service.cpp @@ -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(String8(name).string(), granteeUid, callingUid).c_str()); + return String16(mKeyStore->addGrant(String8(name).string(), callingUid, granteeUid).c_str()); } KeyStoreServiceReturnCode KeyStoreService::ungrant(const String16& name, int32_t granteeUid) { @@ -543,7 +543,7 @@ KeyStoreServiceReturnCode KeyStoreService::ungrant(const String16& name, int32_t return (errno != ENOENT) ? ResponseCode::SYSTEM_ERROR : ResponseCode::KEY_NOT_FOUND; } - return mKeyStore->removeGrant(name8, granteeUid) ? ResponseCode::NO_ERROR + return mKeyStore->removeGrant(name8, callingUid, granteeUid) ? ResponseCode::NO_ERROR : ResponseCode::KEY_NOT_FOUND; } @@ -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) { @@ -1893,8 +1894,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)); + 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; @@ -1907,7 +1912,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; diff --git a/keystore/keystore.cpp b/keystore/keystore.cpp index 8037335f..a61ef733 100644 --- a/keystore/keystore.cpp +++ b/keystore/keystore.cpp @@ -156,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; @@ -310,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; } @@ -332,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; } /* @@ -373,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) { @@ -421,8 +488,11 @@ std::string KeyStore::addGrant(const char* alias, uid_t granterUid, uid_t grante granterUid); } -bool KeyStore::removeGrant(const char* alias, uid_t granteeUid) { - return mGrants.removeByFileAlias(granteeUid, alias); +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, @@ -501,32 +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::KEY_NOT_FOUND) { - return responseCode; - } - - // 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.format("%s/%s", grant->owner_dir_name_.c_str(), - getKeyNameForUid(String8(grant->alias_.c_str()), grant->owner_uid_, type).c_str()); + if (filepath8.isOk()) + return get(filepath8.value().string(), keyBlob, type, userId); - // 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) { @@ -573,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; @@ -583,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; @@ -615,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) { @@ -643,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() { diff --git a/keystore/keystore.h b/keystore/keystore.h index 39761bbc..a0b747ff 100644 --- a/keystore/keystore.h +++ b/keystore/keystore.h @@ -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 @@ -88,7 +90,8 @@ class KeyStore { uid_t userId); std::string addGrant(const char* alias, uid_t granterUid, uid_t granteeUid); - bool removeGrant(const char* alias, 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); diff --git a/keystore/permissions.h b/keystore/permissions.h index 80d0e045..1f7b7a65 100644 --- a/keystore/permissions.h +++ b/keystore/permissions.h @@ -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_ |