vold: remove session keyring workaround for old kernels

The android-4.14-stable and later kernels support the
FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY ioctls.  This
has superseded the old way of adding fscrypt keys to the kernel, which
was to use the add_key() syscall to add keys to the "session" keyring.
On kernels that support the ioctls, Android doesn't use the obsolete
way.  Since upgrading even just to Android 14 requires at minimum a
android-4.14-stable kernel (according to
https://source.android.com/docs/core/architecture/kernel/android-common#compatibility-matrix),
there is no need to support the obsolete way anymore.

Therefore, this commit removes the code that added and removed keys
to/from the session keyring.  Now the ioctls are used unconditionally.

Flag: N/A for the following reasons:
      - Removing obsolete code, which is fairly safe
      - Very early code, so runtime flag cannot be used
      - This topic also removes code from init, which cannot use aconfig
        libraries because they do not support recovery_available

Bug: 311736104
Test: Build and boot Cuttlefish
Change-Id: I0d9abbda77b1ac838ea6f014dbe22ab032c0e5ae
This commit is contained in:
Eric Biggers 2023-11-22 18:22:21 +00:00
parent 1dd20644dc
commit 0e87a83cba
4 changed files with 0 additions and 153 deletions

View file

@ -62,7 +62,6 @@ cc_defaults {
"libincfs",
"libhidlbase",
"libkeymint_support",
"libkeyutils",
"liblog",
"liblogwrap",
"libselinux",

View file

@ -50,7 +50,6 @@
#include <cutils/properties.h>
#include <fscrypt/fscrypt.h>
#include <keyutils.h>
#include <libdm/dm.h>
#include <android-base/file.h>
@ -76,7 +75,6 @@ using android::vold::retrieveOrGenerateKey;
using android::vold::SetDefaultAcl;
using android::vold::SetQuotaInherit;
using android::vold::SetQuotaProjectId;
using android::vold::writeStringToFile;
using namespace android::fscrypt;
using namespace android::dm;
@ -632,27 +630,6 @@ bool fscrypt_create_user_keys(userid_t user_id, int serial, bool ephemeral) {
return true;
}
// "Lock" all encrypted directories whose key has been removed. This is needed
// in the case where the keys are being put in the session keyring (rather in
// the newer filesystem-level keyrings), because removing a key from the session
// keyring doesn't affect inodes in the kernel's inode cache whose per-file key
// was already set up. So to remove the per-file keys and make the files
// "appear encrypted", these inodes must be evicted.
//
// To do this, sync() to clean all dirty inodes, then drop all reclaimable slab
// objects systemwide. This is overkill, but it's the best available method
// currently. Don't use drop_caches mode "3" because that also evicts pagecache
// for in-use files; all files relevant here are already closed and sync'ed.
static void drop_caches_if_needed() {
if (android::vold::isFsKeyringSupported()) {
return;
}
sync();
if (!writeStringToFile("2", "/proc/sys/vm/drop_caches")) {
PLOG(ERROR) << "Failed to drop caches during key eviction";
}
}
// Evicts all the user's keys of one type from all volumes (internal and adoptable).
// This evicts either CE keys or DE keys, depending on which map is passed.
static bool evict_user_keys(std::map<userid_t, UserPolicies>& policy_map, userid_t user_id) {
@ -665,7 +642,6 @@ static bool evict_user_keys(std::map<userid_t, UserPolicies>& policy_map, userid
success &= android::vold::evictKey(BuildDataPath(volume_uuid), policy);
}
policy_map.erase(it);
drop_caches_if_needed();
}
return success;
}

View file

@ -28,7 +28,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <keyutils.h>
#include "KeyStorage.h"
#include "Utils.h"
@ -75,39 +74,6 @@ bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key) {
}
}
static bool isFsKeyringSupportedImpl() {
android::base::unique_fd fd(open("/data", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
// FS_IOC_ADD_ENCRYPTION_KEY with a NULL argument will fail with ENOTTY if
// the ioctl isn't supported. Otherwise it will fail with another error
// code such as EFAULT.
//
// Note that there's no need to check for FS_IOC_REMOVE_ENCRYPTION_KEY,
// since it's guaranteed to be available if FS_IOC_ADD_ENCRYPTION_KEY is.
// There's also no need to check for support on external volumes separately
// from /data, since either the kernel supports the ioctls on all
// fscrypt-capable filesystems or it doesn't.
errno = 0;
(void)ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, NULL);
if (errno == ENOTTY) {
LOG(INFO) << "Kernel doesn't support FS_IOC_ADD_ENCRYPTION_KEY. Falling back to "
"session keyring";
return false;
}
if (errno != EFAULT) {
PLOG(WARNING) << "Unexpected error from FS_IOC_ADD_ENCRYPTION_KEY";
}
LOG(DEBUG) << "Detected support for FS_IOC_ADD_ENCRYPTION_KEY";
return true;
}
// Return true if the kernel supports the ioctls to add/remove fscrypt keys
// directly to/from the filesystem.
bool isFsKeyringSupported(void) {
static bool supported = isFsKeyringSupportedImpl();
return supported;
}
// Get raw keyref - used to make keyname and to pass to ioctl
static std::string generateKeyRef(const uint8_t* key, int length) {
SHA512_CTX c;
@ -127,20 +93,6 @@ static std::string generateKeyRef(const uint8_t* key, int length) {
return std::string((char*)key_ref2, FSCRYPT_KEY_DESCRIPTOR_SIZE);
}
static bool fillKey(const KeyBuffer& key, fscrypt_key* fs_key) {
if (key.size() != FSCRYPT_MAX_KEY_SIZE) {
LOG(ERROR) << "Wrong size key " << key.size();
return false;
}
static_assert(FSCRYPT_MAX_KEY_SIZE == sizeof(fs_key->raw), "Mismatch of max key sizes");
fs_key->mode = 0; // unused by kernel
memcpy(fs_key->raw, key.data(), key.size());
fs_key->size = key.size();
return true;
}
static char const* const NAME_PREFIXES[] = {"ext4", "f2fs", "fscrypt", nullptr};
static std::string keyrefstring(const std::string& raw_ref) {
std::ostringstream o;
for (unsigned char i : raw_ref) {
@ -149,44 +101,6 @@ static std::string keyrefstring(const std::string& raw_ref) {
return o.str();
}
static std::string buildLegacyKeyName(const std::string& prefix, const std::string& raw_ref) {
return prefix + ":" + keyrefstring(raw_ref);
}
// Get the ID of the keyring we store all fscrypt keys in when the kernel is too
// old to support FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY.
static bool fscryptKeyring(key_serial_t* device_keyring) {
*device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "fscrypt", 0);
if (*device_keyring == -1) {
PLOG(ERROR) << "Unable to find device keyring";
return false;
}
return true;
}
// Add an encryption key to the legacy global session keyring.
static bool installKeyLegacy(const KeyBuffer& key, const std::string& raw_ref) {
// Place fscrypt_key into automatically zeroing buffer.
KeyBuffer fsKeyBuffer(sizeof(fscrypt_key));
fscrypt_key& fs_key = *reinterpret_cast<fscrypt_key*>(fsKeyBuffer.data());
if (!fillKey(key, &fs_key)) return false;
key_serial_t device_keyring;
if (!fscryptKeyring(&device_keyring)) return false;
for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
auto ref = buildLegacyKeyName(*name_prefix, raw_ref);
key_serial_t key_id =
add_key("logon", ref.c_str(), (void*)&fs_key, sizeof(fs_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();
}
return true;
}
// Build a struct fscrypt_key_specifier for use in the key management ioctls.
static bool buildKeySpecifier(fscrypt_key_specifier* spec, const EncryptionPolicy& policy) {
switch (policy.options.version) {
@ -230,9 +144,6 @@ bool installKey(const std::string& mountpoint, const EncryptionOptions& options,
// "descriptor", which must be provided by userspace. We use the
// first 8 bytes from the double SHA-512 of the key itself.
policy->key_raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size());
if (!isFsKeyringSupported()) {
return installKeyLegacy(key, policy->key_raw_ref);
}
if (!buildKeySpecifier(&arg->key_spec, *policy)) {
return false;
}
@ -276,29 +187,6 @@ bool installKey(const std::string& mountpoint, const EncryptionOptions& options,
return true;
}
// Remove an encryption key from the legacy global session keyring.
static bool evictKeyLegacy(const std::string& raw_ref) {
key_serial_t device_keyring;
if (!fscryptKeyring(&device_keyring)) return false;
bool success = true;
for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
auto ref = buildLegacyKeyName(*name_prefix, raw_ref);
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;
success = false;
} else {
LOG(DEBUG) << "Unlinked key with serial " << key_serial << " ref " << ref;
}
}
return success;
}
static void waitForBusyFiles(const struct fscrypt_key_specifier key_spec, const std::string ref,
const std::string mountpoint) {
android::base::unique_fd fd(open(mountpoint.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
@ -359,9 +247,6 @@ static void waitForBusyFiles(const struct fscrypt_key_specifier key_spec, const
bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy) {
const std::lock_guard<std::mutex> lock(fscrypt_keyring_mutex);
if (policy.options.version == 1 && !isFsKeyringSupported()) {
return evictKeyLegacy(policy.key_raw_ref);
}
android::base::unique_fd fd(open(mountpoint.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
if (fd == -1) {

View file

@ -43,28 +43,15 @@ bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key);
// be generated.
const KeyGeneration neverGen();
bool isFsKeyringSupported(void);
// Install a file-based encryption key to the kernel, for use by encrypted files
// on the specified filesystem using the specified encryption policy version.
//
// 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 *policy
// to the EncryptionPolicy used to refer to this key.
bool installKey(const std::string& mountpoint, const android::fscrypt::EncryptionOptions& options,
const KeyBuffer& key, android::fscrypt::EncryptionPolicy* policy);
// Evict a file-based encryption key from the kernel.
//
// We use FS_IOC_REMOVE_ENCRYPTION_KEY if the kernel supports it. Otherwise we
// 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 android::fscrypt::EncryptionPolicy& policy);
// Retrieves the key from the named directory, or generates it if it doesn't