vold: support v2 encryption policies

Add support for setting v2 encryption policies when configured in the
fstab (for internal storage) or in system properties (for adoptable
storage), and for installing and evicting the keys for such policies.

v2 policies support the same encryption modes and flags as v1 policies,
but internally they use a more standard, secure, and flexible KDF.  Due
to this, some future features will be supported by v2 policies only.

Bug: 140500999
Test: Configured a device to use v2 encryption policies (applied the
      needed kernel patches and added
      "fileencryption=aes-256-xts:aes-256-cts:v2" to fstab, and set the
      corresponding system properties for adoptable storage).  Wiped
      userdata, booted device and checked logs to verify that v2
      policies were being used.

      Also enabled virtual SD card and formatted as adoptable storage;
      verified it works and that v2 policies were being used on it.

      Also created, started, and stopped a 2nd user and verified their
      keys were evicted.

      Also verified that the device comes up again after rebooting.

      Also verified that a device using v1 encryption policies continues
      to work, both with and without an updated kernel -- including
      stopping a user so that their keys get evicted.

Change-Id: If64028d8580584b2c33c614cabd5d6b93657f608
This commit is contained in:
Eric Biggers 2019-09-30 13:06:47 -07:00
parent f3dc4203dd
commit 83a73d7d50
3 changed files with 150 additions and 51 deletions

View file

@ -57,6 +57,7 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
using android::base::StringPrintf;
@ -70,7 +71,10 @@ namespace {
struct PolicyKeyRef {
std::string contents_mode;
std::string filenames_mode;
int policy_version;
std::string key_raw_ref;
PolicyKeyRef() : policy_version(0) {}
};
const std::string device_key_dir = std::string() + DATA_MNT_POINT + fscrypt_unencrypted_folder;
@ -199,14 +203,57 @@ static bool read_and_fixate_user_ce_key(userid_t user_id,
return false;
}
// Retrieve the options to use for encryption policies on the /data filesystem.
static void get_data_file_encryption_options(PolicyKeyRef* key_ref) {
auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
if (entry == nullptr) {
return;
}
key_ref->contents_mode = entry->file_contents_mode;
key_ref->filenames_mode = entry->file_names_mode;
key_ref->policy_version = entry->file_policy_version;
}
// Retrieve the version to use for encryption policies on the /data filesystem.
static int get_data_file_policy_version(void) {
auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
if (entry == nullptr) {
return 0;
}
return entry->file_policy_version;
}
// Retrieve the options to use for encryption policies on adoptable storage.
static bool get_volume_file_encryption_options(PolicyKeyRef* key_ref) {
key_ref->contents_mode =
android::base::GetProperty("ro.crypto.volume.contents_mode", "aes-256-xts");
key_ref->filenames_mode =
android::base::GetProperty("ro.crypto.volume.filenames_mode", "aes-256-heh");
key_ref->policy_version = 1;
std::string raw_flags = android::base::GetProperty("ro.crypto.volume.flags", "");
auto flags = android::base::Split(raw_flags, "+");
for (const auto& flag : flags) {
if (flag == "v1") {
key_ref->policy_version = 1;
} else if (flag == "v2") {
key_ref->policy_version = 2;
} else {
LOG(ERROR) << "Unknown flag in ro.crypto.volume.flags: " << flag;
return false;
}
}
return true;
}
// Install a key for use by encrypted files on the /data filesystem.
static bool install_data_key(const KeyBuffer& key, std::string* raw_ref) {
return android::vold::installKey(key, DATA_MNT_POINT, raw_ref);
return android::vold::installKey(key, DATA_MNT_POINT, get_data_file_policy_version(), raw_ref);
}
// Evict a key for use by encrypted files on the /data filesystem.
static bool evict_data_key(const std::string& raw_ref) {
return android::vold::evictKey(DATA_MNT_POINT, raw_ref);
return android::vold::evictKey(DATA_MNT_POINT, raw_ref, get_data_file_policy_version());
}
static bool read_and_install_user_ce_key(userid_t user_id,
@ -286,19 +333,10 @@ static bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, useri
return true;
}
static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) {
auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
if (entry == nullptr) {
return;
}
key_ref->contents_mode = entry->file_contents_mode;
key_ref->filenames_mode = entry->file_names_mode;
}
static bool ensure_policy(const PolicyKeyRef& key_ref, const std::string& path) {
return fscrypt_policy_ensure(path.c_str(), key_ref.key_raw_ref.data(),
key_ref.key_raw_ref.size(), key_ref.contents_mode.c_str(),
key_ref.filenames_mode.c_str()) == 0;
key_ref.filenames_mode.c_str(), key_ref.policy_version) == 0;
}
static bool is_numeric(const char* name) {
@ -354,14 +392,18 @@ bool fscrypt_initialize_systemwide_keys() {
}
PolicyKeyRef device_ref;
if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path,
device_key_temp, "", &device_ref.key_raw_ref))
return false;
get_data_file_encryption_modes(&device_ref);
get_data_file_encryption_options(&device_ref);
std::string modestring = device_ref.contents_mode + ":" + device_ref.filenames_mode;
std::string mode_filename = std::string("/data") + fscrypt_key_mode;
if (!android::vold::writeStringToFile(modestring, mode_filename)) return false;
if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path,
device_key_temp, "", device_ref.policy_version,
&device_ref.key_raw_ref))
return false;
std::string options_string =
StringPrintf("%s:%s:v%d", device_ref.contents_mode.c_str(),
device_ref.filenames_mode.c_str(), device_ref.policy_version);
std::string options_filename = std::string("/data") + fscrypt_key_mode;
if (!android::vold::writeStringToFile(options_string, options_filename)) return false;
std::string ref_filename = std::string("/data") + fscrypt_key_ref;
if (!android::vold::writeStringToFile(device_ref.key_raw_ref, ref_filename)) return false;
@ -560,14 +602,12 @@ static bool read_or_create_volkey(const std::string& misc_path, const std::strin
return false;
}
android::vold::KeyAuthentication auth("", secdiscardable_hash);
if (!android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp", volume_uuid,
&key_ref->key_raw_ref))
return false;
key_ref->contents_mode =
android::base::GetProperty("ro.crypto.volume.contents_mode", "aes-256-xts");
key_ref->filenames_mode =
android::base::GetProperty("ro.crypto.volume.filenames_mode", "aes-256-heh");
return true;
if (!get_volume_file_encryption_options(key_ref)) return false;
return android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp",
volume_uuid, key_ref->policy_version,
&key_ref->key_raw_ref);
}
static bool destroy_volkey(const std::string& misc_path, const std::string& volume_uuid) {
@ -715,7 +755,7 @@ bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_
PolicyKeyRef de_ref;
if (volume_uuid.empty()) {
if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_ref.key_raw_ref)) return false;
get_data_file_encryption_modes(&de_ref);
get_data_file_encryption_options(&de_ref);
if (!ensure_policy(de_ref, system_de_path)) return false;
if (!ensure_policy(de_ref, misc_de_path)) return false;
if (!ensure_policy(de_ref, vendor_de_path)) return false;
@ -746,7 +786,7 @@ bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_
PolicyKeyRef ce_ref;
if (volume_uuid.empty()) {
if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_ref.key_raw_ref)) return false;
get_data_file_encryption_modes(&ce_ref);
get_data_file_encryption_options(&ce_ref);
if (!ensure_policy(ce_ref, system_ce_path)) return false;
if (!ensure_policy(ce_ref, misc_ce_path)) return false;
if (!ensure_policy(ce_ref, vendor_ce_path)) return false;

View file

@ -163,28 +163,80 @@ static bool installKeyLegacy(const KeyBuffer& key, const std::string& raw_ref) {
return true;
}
// Build a struct fscrypt_key_specifier for use in the key management ioctls.
static bool buildKeySpecifier(fscrypt_key_specifier* spec, const std::string& raw_ref,
int policy_version) {
switch (policy_version) {
case 1:
if (raw_ref.size() != FSCRYPT_KEY_DESCRIPTOR_SIZE) {
LOG(ERROR) << "Invalid key specifier size for v1 encryption policy: "
<< raw_ref.size();
return false;
}
spec->type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
memcpy(spec->u.descriptor, raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE);
return true;
case 2:
if (raw_ref.size() != FSCRYPT_KEY_IDENTIFIER_SIZE) {
LOG(ERROR) << "Invalid key specifier size for v2 encryption policy: "
<< raw_ref.size();
return false;
}
spec->type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
memcpy(spec->u.identifier, raw_ref.c_str(), FSCRYPT_KEY_IDENTIFIER_SIZE);
return true;
default:
LOG(ERROR) << "Invalid encryption policy version: " << policy_version;
return false;
}
}
// Install a file-based encryption key to the kernel, for use by encrypted files
// on the specified filesystem.
// on the specified filesystem using the specified encryption policy version.
//
// We use FS_IOC_ADD_ENCRYPTION_KEY if the kernel supports it. Otherwise we add
// the key to the legacy global session keyring.
// For v1 policies, we use FS_IOC_ADD_ENCRYPTION_KEY if the kernel supports it.
// Otherwise we add the key to the legacy global session keyring.
//
// For v2 policies, we always use FS_IOC_ADD_ENCRYPTION_KEY; it's the only way
// the kernel supports.
//
// Returns %true on success, %false on failure. On success also sets *raw_ref
// to the raw key reference for use in the encryption policy.
bool installKey(const KeyBuffer& key, const std::string& mountpoint, std::string* raw_ref) {
*raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size());
if (!isFsKeyringSupported()) {
return installKeyLegacy(key, *raw_ref);
}
bool installKey(const KeyBuffer& key, const std::string& mountpoint, int policy_version,
std::string* raw_ref) {
// Put the fscrypt_add_key_arg in an automatically-zeroing buffer, since we
// have to copy the raw key into it.
KeyBuffer arg_buf(sizeof(struct fscrypt_add_key_arg) + key.size(), 0);
struct fscrypt_add_key_arg* arg = (struct fscrypt_add_key_arg*)arg_buf.data();
arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
memcpy(arg->key_spec.u.descriptor, raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE);
// Initialize the "key specifier", which is like a name for the key.
switch (policy_version) {
case 1:
// A key for a v1 policy is specified by an arbitrary 8-byte
// "descriptor", which must be provided by userspace. We use the
// first 8 bytes from the double SHA-512 of the key itself.
*raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size());
if (!isFsKeyringSupported()) {
return installKeyLegacy(key, *raw_ref);
}
if (!buildKeySpecifier(&arg->key_spec, *raw_ref, policy_version)) {
return false;
}
break;
case 2:
// A key for a v2 policy is specified by an 16-byte "identifier",
// which is a cryptographic hash of the key itself which the kernel
// computes and returns. Any user-provided value is ignored; we
// just need to set the specifier type to indicate that we're adding
// this type of key.
arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
break;
default:
LOG(ERROR) << "Invalid encryption policy version: " << policy_version;
return false;
}
// Provide the raw key.
arg->raw_size = key.size();
memcpy(arg->raw, key.data(), key.size());
@ -195,11 +247,14 @@ bool installKey(const KeyBuffer& key, const std::string& mountpoint, std::string
}
if (ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, arg) != 0) {
PLOG(ERROR) << "Failed to install fscrypt key with ref " << keyrefstring(*raw_ref) << " to "
<< mountpoint;
PLOG(ERROR) << "Failed to install fscrypt key to " << mountpoint;
return false;
}
if (arg->key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
// Retrieve the key identifier that the kernel computed.
*raw_ref = std::string((char*)arg->key_spec.u.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE);
}
LOG(DEBUG) << "Installed fscrypt key with ref " << keyrefstring(*raw_ref) << " to "
<< mountpoint;
return true;
@ -234,8 +289,8 @@ static bool evictKeyLegacy(const std::string& raw_ref) {
// remove the key from the legacy global session keyring.
//
// In the latter case, the caller is responsible for dropping caches.
bool evictKey(const std::string& mountpoint, const std::string& raw_ref) {
if (!isFsKeyringSupported()) {
bool evictKey(const std::string& mountpoint, const std::string& raw_ref, int policy_version) {
if (policy_version == 1 && !isFsKeyringSupported()) {
return evictKeyLegacy(raw_ref);
}
@ -248,8 +303,9 @@ bool evictKey(const std::string& mountpoint, const std::string& raw_ref) {
struct fscrypt_remove_key_arg arg;
memset(&arg, 0, sizeof(arg));
arg.key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
memcpy(arg.key_spec.u.descriptor, raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE);
if (!buildKeySpecifier(&arg.key_spec, raw_ref, policy_version)) {
return false;
}
std::string ref = keyrefstring(raw_ref);
@ -271,7 +327,8 @@ bool evictKey(const std::string& mountpoint, const std::string& raw_ref) {
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
const std::string& key_path, const std::string& tmp_path,
const std::string& volume_uuid, std::string* key_ref) {
const std::string& volume_uuid, int policy_version,
std::string* key_ref) {
KeyBuffer key;
if (pathExists(key_path)) {
LOG(DEBUG) << "Key exists, using: " << key_path;
@ -286,7 +343,7 @@ bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_a
if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false;
}
if (!installKey(key, BuildDataPath(volume_uuid), key_ref)) {
if (!installKey(key, BuildDataPath(volume_uuid), policy_version, key_ref)) {
LOG(ERROR) << "Failed to install key in " << key_path;
return false;
}

View file

@ -30,11 +30,13 @@ bool randomKey(KeyBuffer* key);
bool isFsKeyringSupported(void);
bool installKey(const KeyBuffer& key, const std::string& mountpoint, std::string* raw_ref);
bool evictKey(const std::string& mountpoint, const std::string& raw_ref);
bool installKey(const KeyBuffer& key, const std::string& mountpoint, int policy_version,
std::string* raw_ref);
bool evictKey(const std::string& mountpoint, const std::string& raw_ref, int policy_version);
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
const std::string& key_path, const std::string& tmp_path,
const std::string& volume_uuid, std::string* key_ref);
const std::string& volume_uuid, int policy_version,
std::string* key_ref);
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
KeyBuffer* key, bool keepOld = true);