Refactor to lay the groundwork for metadata encryption
Bug: 26778031
Test: Angler, Marlin build and boot
Change-Id: Ic136dfe6195a650f7db76d3489f36da6a1929dc5
diff --git a/Ext4Crypt.cpp b/Ext4Crypt.cpp
index 88bedd0..cb5f6d0 100644
--- a/Ext4Crypt.cpp
+++ b/Ext4Crypt.cpp
@@ -17,22 +17,21 @@
#include "Ext4Crypt.h"
#include "KeyStorage.h"
+#include "KeyUtil.h"
#include "Utils.h"
#include <algorithm>
-#include <iomanip>
#include <map>
#include <set>
#include <sstream>
#include <string>
+#include <vector>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
-#include <openssl/sha.h>
#include <selinux/android.h>
-#include <stdio.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -45,8 +44,10 @@
#define MANAGE_MISC_DIRS 0
#include <cutils/fs.h>
-#include <ext4_utils/ext4_crypt.h>
+#include <cutils/properties.h>
+
#include <ext4_utils/key_control.h>
+#include <ext4_utils/ext4_crypt.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -79,19 +80,6 @@
// TODO abolish this map, per b/26948053
std::map<userid_t, std::string> s_ce_keys;
-// ext4enc:TODO get this const from somewhere good
-const int EXT4_KEY_DESCRIPTOR_SIZE = 8;
-
-// ext4enc:TODO Include structure from somewhere sensible
-// MUST be in sync with ext4_crypto.c in kernel
-constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1;
-constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
-constexpr int EXT4_MAX_KEY_SIZE = 64;
-struct ext4_encryption_key {
- uint32_t mode;
- char raw[EXT4_MAX_KEY_SIZE];
- uint32_t size;
-};
}
static bool e4crypt_is_emulated() {
@@ -102,85 +90,6 @@
return (value == nullptr) ? "null" : value;
}
-// Get raw keyref - used to make keyname and to pass to ioctl
-static std::string generate_key_ref(const char* key, int length) {
- SHA512_CTX c;
-
- SHA512_Init(&c);
- SHA512_Update(&c, key, length);
- unsigned char key_ref1[SHA512_DIGEST_LENGTH];
- SHA512_Final(key_ref1, &c);
-
- SHA512_Init(&c);
- SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
- unsigned char key_ref2[SHA512_DIGEST_LENGTH];
- SHA512_Final(key_ref2, &c);
-
- static_assert(EXT4_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
- "Hash too short for descriptor");
- return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
-}
-
-static bool fill_key(const std::string& key, ext4_encryption_key* ext4_key) {
- if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) {
- LOG(ERROR) << "Wrong size key " << key.size();
- return false;
- }
- static_assert(EXT4_AES_256_XTS_KEY_SIZE <= sizeof(ext4_key->raw), "Key too long!");
- ext4_key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
- ext4_key->size = key.size();
- memset(ext4_key->raw, 0, sizeof(ext4_key->raw));
- memcpy(ext4_key->raw, key.data(), key.size());
- return true;
-}
-
-static std::string keyname(const std::string& raw_ref) {
- std::ostringstream o;
- o << "ext4:";
- for (auto i : raw_ref) {
- o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
- }
- return o.str();
-}
-
-// Get the keyring we store all keys in
-static bool e4crypt_keyring(key_serial_t* device_keyring) {
- *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0);
- if (*device_keyring == -1) {
- PLOG(ERROR) << "Unable to find device keyring";
- return false;
- }
- return true;
-}
-
-// Install password into global keyring
-// Return raw key reference for use in policy
-static bool install_key(const std::string& key, std::string* raw_ref) {
- ext4_encryption_key ext4_key;
- if (!fill_key(key, &ext4_key)) return false;
- *raw_ref = generate_key_ref(ext4_key.raw, ext4_key.size);
- auto ref = keyname(*raw_ref);
- key_serial_t device_keyring;
- if (!e4crypt_keyring(&device_keyring)) return false;
- key_serial_t key_id =
- add_key("logon", ref.c_str(), (void*)&ext4_key, sizeof(ext4_key), device_keyring);
- if (key_id == -1) {
- PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring;
- return false;
- }
- LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
- << " in process " << getpid();
-
- // *TODO* Remove this code when kernel is fixed - see b/28373400
- // Kernel preserves caches across a key insertion with ext4ice, which leads
- // to contradictory dirents
- if (!android::base::WriteStringToFile("3", "/proc/sys/vm/drop_caches")) {
- PLOG(ERROR) << "Failed to drop_caches";
- }
-
- return true;
-}
-
static std::string get_de_key_path(userid_t user_id) {
return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id);
}
@@ -280,7 +189,7 @@
std::string ce_key;
if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
std::string ce_raw_ref;
- if (!install_key(ce_key, &ce_raw_ref)) return false;
+ if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false;
s_ce_keys[user_id] = ce_key;
s_ce_key_raw_refs[user_id] = ce_raw_ref;
LOG(DEBUG) << "Installed ce key for user " << user_id;
@@ -305,43 +214,12 @@
return true;
}
-static bool random_key(std::string* key) {
- if (android::vold::ReadRandomBytes(EXT4_AES_256_XTS_KEY_SIZE, *key) != 0) {
- // TODO status_t plays badly with PLOG, fix it.
- LOG(ERROR) << "Random read failed";
- return false;
- }
- return true;
-}
-
-static bool path_exists(const std::string& path) {
- return access(path.c_str(), F_OK) == 0;
-}
-
// NB this assumes that there is only one thread listening for crypt commands, because
// it creates keys in a fixed location.
-static bool store_key(const std::string& key_path, const std::string& tmp_path,
- const android::vold::KeyAuthentication& auth, const std::string& key) {
- if (path_exists(key_path)) {
- LOG(ERROR) << "Already exists, cannot create key at: " << key_path;
- return false;
- }
- if (path_exists(tmp_path)) {
- android::vold::destroyKey(tmp_path); // May be partially created so ignore errors
- }
- if (!android::vold::storeKey(tmp_path, auth, key)) return false;
- if (rename(tmp_path.c_str(), key_path.c_str()) != 0) {
- PLOG(ERROR) << "Unable to move new key to location: " << key_path;
- return false;
- }
- LOG(DEBUG) << "Created key " << key_path;
- return true;
-}
-
static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
std::string de_key, ce_key;
- if (!random_key(&de_key)) return false;
- if (!random_key(&ce_key)) return false;
+ if (!android::vold::randomKey(&de_key)) return false;
+ if (!android::vold::randomKey(&ce_key)) return false;
if (create_ephemeral) {
// If the key should be created as ephemeral, don't store it.
s_ephemeral_users.insert(user_id);
@@ -351,18 +229,18 @@
auto const paths = get_ce_key_paths(directory_path);
std::string ce_key_path;
if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
- if (!store_key(ce_key_path, user_key_temp,
+ if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp,
kEmptyAuthentication, ce_key)) return false;
fixate_user_ce_key(directory_path, ce_key_path, paths);
// Write DE key second; once this is written, all is good.
- if (!store_key(get_de_key_path(user_id), user_key_temp,
+ if (!android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp,
kEmptyAuthentication, de_key)) return false;
}
std::string de_raw_ref;
- if (!install_key(de_key, &de_raw_ref)) return false;
+ if (!android::vold::installKey(de_key, &de_raw_ref)) return false;
s_de_key_raw_refs[user_id] = de_raw_ref;
std::string ce_raw_ref;
- if (!install_key(ce_key, &ce_raw_ref)) return false;
+ if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false;
s_ce_keys[user_id] = ce_key;
s_ce_key_raw_refs[user_id] = ce_raw_ref;
LOG(DEBUG) << "Created keys for user " << user_id;
@@ -429,7 +307,7 @@
std::string key;
if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
std::string raw_ref;
- if (!install_key(key, &raw_ref)) return false;
+ if (!android::vold::installKey(key, &raw_ref)) return false;
s_de_key_raw_refs[user_id] = raw_ref;
LOG(DEBUG) << "Installed de key for user " << user_id;
}
@@ -458,28 +336,16 @@
return false;
}
- std::string device_key;
- if (path_exists(device_key_path)) {
- if (!android::vold::retrieveKey(device_key_path,
- kEmptyAuthentication, &device_key)) return false;
- } else {
- LOG(INFO) << "Creating new key";
- if (!random_key(&device_key)) return false;
- if (!store_key(device_key_path, device_key_temp,
- kEmptyAuthentication, device_key)) return false;
- }
-
std::string device_key_ref;
- if (!install_key(device_key, &device_key_ref)) {
- LOG(ERROR) << "Failed to install device key";
- return false;
- }
+ if (!android::vold::retrieveAndInstallKey(true,
+ device_key_path, device_key_temp, &device_key_ref)) return false;
std::string ref_filename = std::string("/data") + e4crypt_key_ref;
if (!android::base::WriteStringToFile(device_key_ref, ref_filename)) {
- PLOG(ERROR) << "Cannot save key reference";
+ PLOG(ERROR) << "Cannot save key reference to:" << ref_filename;
return false;
}
+ LOG(INFO) << "Wrote system DE key reference to:" << ref_filename;
s_global_de_initialized = true;
return true;
@@ -491,7 +357,7 @@
if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
- if (!path_exists(get_de_key_path(0))) {
+ if (!android::vold::pathExists(get_de_key_path(0))) {
if (!create_and_install_user_keys(0, false)) return false;
}
// TODO: switch to loading only DE_0 here once framework makes
@@ -533,31 +399,13 @@
return true;
}
-static bool evict_key(const std::string &raw_ref) {
- auto ref = keyname(raw_ref);
- key_serial_t device_keyring;
- if (!e4crypt_keyring(&device_keyring)) return false;
- auto key_serial = keyctl_search(device_keyring, "logon", ref.c_str(), 0);
-
- // Unlink the key from the keyring. Prefer unlinking to revoking or
- // invalidating, since unlinking is actually no less secure currently, and
- // it avoids bugs in certain kernel versions where the keyring key is
- // referenced from places it shouldn't be.
- if (keyctl_unlink(key_serial, device_keyring) != 0) {
- PLOG(ERROR) << "Failed to unlink key with serial " << key_serial << " ref " << ref;
- return false;
- }
- LOG(DEBUG) << "Unlinked key with serial " << key_serial << " ref " << ref;
- return true;
-}
-
static bool evict_ce_key(userid_t user_id) {
- s_ce_keys.erase(user_id);
+ s_ce_keys.erase(user_id);
bool success = true;
std::string raw_ref;
// If we haven't loaded the CE key, no need to evict it.
if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) {
- success &= evict_key(raw_ref);
+ success &= android::vold::evictKey(raw_ref);
}
s_ce_key_raw_refs.erase(user_id);
return success;
@@ -571,7 +419,8 @@
bool success = true;
std::string raw_ref;
success &= evict_ce_key(user_id);
- success &= lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref) && evict_key(raw_ref);
+ success &= lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref)
+ && android::vold::evictKey(raw_ref);
s_de_key_raw_refs.erase(user_id);
auto it = s_ephemeral_users.find(user_id);
if (it != s_ephemeral_users.end()) {
@@ -581,7 +430,7 @@
success &= android::vold::destroyKey(path);
}
auto de_key_path = get_de_key_path(user_id);
- if (path_exists(de_key_path)) {
+ if (android::vold::pathExists(de_key_path)) {
success &= android::vold::destroyKey(de_key_path);
} else {
LOG(INFO) << "Not present so not erasing: " << de_key_path;
@@ -653,7 +502,7 @@
auto const paths = get_ce_key_paths(directory_path);
std::string ce_key_path;
if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
- if (!store_key(ce_key_path, user_key_temp, auth, ce_key)) return false;
+ if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, auth, ce_key)) return false;
return true;
}