Add Support for metadata key with rollback

This adds the ability to upgrade a key and retain the
old one for rollback purposes. We delete the old key
if we boot successfully and delete the new key if we
do not.

Test: Enable checkpointing and test rolling back
      between two versions
Bug: 111020314

Change-Id: I19f31a1ac06a811c0644fc956e61b5ca84e7241a
This commit is contained in:
Daniel Rosenberg 2018-12-14 01:08:10 -08:00
parent ffa1bb0370
commit 690d6de5bf
5 changed files with 83 additions and 22 deletions

View file

@ -202,7 +202,7 @@ static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
km::KeyPurpose purpose, const km::AuthorizationSet& keyParams, km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
const km::AuthorizationSet& opParams, const km::AuthorizationSet& opParams,
const km::HardwareAuthToken& authToken, const km::HardwareAuthToken& authToken,
km::AuthorizationSet* outParams) { km::AuthorizationSet* outParams, bool keepOld) {
auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob; auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
std::string kmKey; std::string kmKey;
if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation(); if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
@ -219,12 +219,14 @@ static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation(); if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded; auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation(); if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) { if (!keepOld) {
PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath; if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
return KeymasterOperation(); PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
} return KeymasterOperation();
if (!keymaster.deleteKey(kmKey)) { }
LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir; if (!keymaster.deleteKey(kmKey)) {
LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
}
} }
kmKey = newKey; kmKey = newKey;
LOG(INFO) << "Key upgraded: " << dir; LOG(INFO) << "Key upgraded: " << dir;
@ -233,12 +235,12 @@ static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir, static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
const km::AuthorizationSet& keyParams, const km::AuthorizationSet& keyParams,
const km::HardwareAuthToken& authToken, const km::HardwareAuthToken& authToken, const KeyBuffer& message,
const KeyBuffer& message, std::string* ciphertext) { std::string* ciphertext, bool keepOld) {
km::AuthorizationSet opParams; km::AuthorizationSet opParams;
km::AuthorizationSet outParams; km::AuthorizationSet outParams;
auto opHandle = auto opHandle = begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken,
begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken, &outParams); &outParams, keepOld);
if (!opHandle) return false; if (!opHandle) return false;
auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE); auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
if (!nonceBlob.isOk()) { if (!nonceBlob.isOk()) {
@ -262,13 +264,14 @@ static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir
static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir, static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
const km::AuthorizationSet& keyParams, const km::AuthorizationSet& keyParams,
const km::HardwareAuthToken& authToken, const km::HardwareAuthToken& authToken,
const std::string& ciphertext, KeyBuffer* message) { const std::string& ciphertext, KeyBuffer* message,
bool keepOld) {
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES); auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES); auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE, auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
km::support::blob2hidlVec(nonce)); km::support::blob2hidlVec(nonce));
auto opHandle = auto opHandle = begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken,
begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken, nullptr); nullptr, keepOld);
if (!opHandle) return false; if (!opHandle) return false;
if (!opHandle.updateCompletely(bodyAndMac, message)) return false; if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
if (!opHandle.finish(nullptr)) return false; if (!opHandle.finish(nullptr)) return false;
@ -474,7 +477,8 @@ bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBu
km::AuthorizationSet keyParams; km::AuthorizationSet keyParams;
km::HardwareAuthToken authToken; km::HardwareAuthToken authToken;
std::tie(keyParams, authToken) = beginParams(auth, appId); std::tie(keyParams, authToken) = beginParams(auth, appId);
if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey)) if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey,
false))
return false; return false;
} else { } else {
if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false; if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
@ -502,7 +506,8 @@ bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path
return true; return true;
} }
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key) { bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
bool keepOld) {
std::string version; std::string version;
if (!readFileToString(dir + "/" + kFn_version, &version)) return false; if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
if (version != kCurrentVersion) { if (version != kCurrentVersion) {
@ -527,7 +532,8 @@ bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffe
km::AuthorizationSet keyParams; km::AuthorizationSet keyParams;
km::HardwareAuthToken authToken; km::HardwareAuthToken authToken;
std::tie(keyParams, authToken) = beginParams(auth, appId); std::tie(keyParams, authToken) = beginParams(auth, appId);
if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key)) if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key,
keepOld))
return false; return false;
} else { } else {
if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false; if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;

View file

@ -61,7 +61,8 @@ bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path
const KeyAuthentication& auth, const KeyBuffer& key); const KeyAuthentication& auth, const KeyBuffer& key);
// Retrieve the key from the named directory. // Retrieve the key from the named directory.
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key); bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
bool keepOld = false);
// Securely destroy the key stored in the named directory and delete the directory. // Securely destroy the key stored in the named directory and delete the directory.
bool destroyKey(const std::string& dir); bool destroyKey(const std::string& dir);

View file

@ -169,10 +169,10 @@ bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_a
} }
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path, bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
KeyBuffer* key) { KeyBuffer* key, bool keepOld) {
if (pathExists(key_path)) { if (pathExists(key_path)) {
LOG(DEBUG) << "Key exists, using: " << key_path; LOG(DEBUG) << "Key exists, using: " << key_path;
if (!retrieveKey(key_path, kEmptyAuthentication, key)) return false; if (!retrieveKey(key_path, kEmptyAuthentication, key, keepOld)) return false;
} else { } else {
if (!create_if_absent) { if (!create_if_absent) {
LOG(ERROR) << "No key found in " << key_path; LOG(ERROR) << "No key found in " << key_path;

View file

@ -33,7 +33,7 @@ bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_a
const std::string& key_path, const std::string& tmp_path, const std::string& key_path, const std::string& tmp_path,
std::string* key_ref); std::string* key_ref);
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path, bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
KeyBuffer* key); KeyBuffer* key, bool keepOld = true);
} // namespace vold } // namespace vold
} // namespace android } // namespace android

View file

@ -30,6 +30,7 @@
#include <linux/dm-ioctl.h> #include <linux/dm-ioctl.h>
#include <android-base/file.h>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/properties.h> #include <android-base/properties.h>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
@ -40,6 +41,7 @@
#include "EncryptInplace.h" #include "EncryptInplace.h"
#include "KeyStorage.h" #include "KeyStorage.h"
#include "KeyUtil.h" #include "KeyUtil.h"
#include "Keymaster.h"
#include "Utils.h" #include "Utils.h"
#include "VoldUtil.h" #include "VoldUtil.h"
#include "secontext.h" #include "secontext.h"
@ -52,6 +54,9 @@ using android::vold::KeyBuffer;
static const std::string kDmNameUserdata = "userdata"; static const std::string kDmNameUserdata = "userdata";
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) { static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
// fs_mgr_do_mount runs fsck. Use setexeccon to run trusted // fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
// partitions in the fsck domain. // partitions in the fsck domain.
@ -74,12 +79,41 @@ static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
return true; return true;
} }
namespace android {
namespace vold {
// Note: It is possible to orphan a key if it is removed before deleting
// Update this once keymaster APIs change, and we have a proper commit.
static void commit_key(std::string dir) {
while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
LOG(ERROR) << "Wait for boot timed out";
}
Keymaster keymaster;
auto keyPath = dir + "/" + kFn_keymaster_key_blob;
auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
std::string key;
if (!android::base::ReadFileToString(keyPath, &key)) {
LOG(ERROR) << "Failed to read old key: " << dir;
return;
}
if (rename(newKeyPath.c_str(), keyPath.c_str()) != 0) {
PLOG(ERROR) << "Unable to move upgraded key to location: " << keyPath;
return;
}
if (!keymaster.deleteKey(key)) {
LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
}
LOG(INFO) << "Old Key deleted: " << dir;
}
static bool read_key(struct fstab_rec const* data_rec, bool create_if_absent, KeyBuffer* key) { static bool read_key(struct fstab_rec const* data_rec, bool create_if_absent, KeyBuffer* key) {
if (!data_rec->key_dir) { if (!data_rec->key_dir) {
LOG(ERROR) << "Failed to get key_dir"; LOG(ERROR) << "Failed to get key_dir";
return false; return false;
} }
std::string key_dir = data_rec->key_dir; std::string key_dir = data_rec->key_dir;
std::string sKey;
auto dir = key_dir + "/key"; auto dir = key_dir + "/key";
LOG(DEBUG) << "key_dir/key: " << dir; LOG(DEBUG) << "key_dir/key: " << dir;
if (fs_mkdirs(dir.c_str(), 0700)) { if (fs_mkdirs(dir.c_str(), 0700)) {
@ -87,10 +121,30 @@ static bool read_key(struct fstab_rec const* data_rec, bool create_if_absent, Ke
return false; return false;
} }
auto temp = key_dir + "/tmp"; auto temp = key_dir + "/tmp";
if (!android::vold::retrieveKey(create_if_absent, dir, temp, key)) return false; auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
/* If we have a leftover upgraded key, delete it.
* We either failed an update and must return to the old key,
* or we rebooted before commiting the keys in a freak accident.
* Either way, we can re-upgrade the key if we need to.
*/
Keymaster keymaster;
if (pathExists(newKeyPath)) {
if (!android::base::ReadFileToString(newKeyPath, &sKey))
LOG(ERROR) << "Failed to read old key: " << dir;
else if (!keymaster.deleteKey(sKey))
LOG(ERROR) << "Old key deletion failed, continuing anyway: " << dir;
else
unlink(newKeyPath.c_str());
}
bool needs_cp = cp_needsCheckpoint();
if (!android::vold::retrieveKey(create_if_absent, dir, temp, key, needs_cp)) return false;
if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach();
return true; return true;
} }
} // namespace vold
} // namespace android
static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) { static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) {
KeyBuffer hex_key; KeyBuffer hex_key;
if (android::vold::StrToHex(key, hex_key) != android::OK) { if (android::vold::StrToHex(key, hex_key) != android::OK) {