Merge "Merge Android 14" into main

This commit is contained in:
Xin Li 2023-10-06 05:34:19 +00:00 committed by Gerrit Code Review
commit ab9fe7e68a
9 changed files with 313 additions and 168 deletions

View file

@ -16,6 +16,8 @@
#define LOG_TAG "Checkpoint" #define LOG_TAG "Checkpoint"
#include "Checkpoint.h" #include "Checkpoint.h"
#include "FsCrypt.h"
#include "KeyStorage.h"
#include "VoldUtil.h" #include "VoldUtil.h"
#include "VolumeManager.h" #include "VolumeManager.h"
@ -78,6 +80,18 @@ bool setBowState(std::string const& block_device, std::string const& state) {
return true; return true;
} }
// Do any work that was deferred until the userdata filesystem checkpoint was
// committed. This work involves the deletion of resources that aren't covered
// by the userdata filesystem checkpoint, e.g. Keystore keys.
void DoCheckpointCommittedWork() {
// Take the crypt lock to provide synchronization with the Binder calls that
// operate on key directories.
std::lock_guard<std::mutex> lock(VolumeManager::Instance()->getCryptLock());
DeferredCommitKeystoreKeys();
fscrypt_deferred_fixate_ce_keys();
}
} // namespace } // namespace
Status cp_supportsCheckpoint(bool& result) { Status cp_supportsCheckpoint(bool& result) {
@ -206,6 +220,7 @@ Status cp_commitChanges() {
if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str)) if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
return error(err_str.c_str()); return error(err_str.c_str());
std::thread(DoCheckpointCommittedWork).detach();
return Status::ok(); return Status::ok();
} }
@ -582,21 +597,31 @@ void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector<char>&
// Read from the device // Read from the device
// If we are validating, the read occurs as though the relocations had happened // If we are validating, the read occurs as though the relocations had happened
// returns the amount asked for or an empty buffer on error. Partial reads are considered a failure
std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating, std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating,
sector_t sector, uint32_t size, uint32_t block_size) { sector_t sector, uint32_t size, uint32_t block_size) {
if (!validating) { if (!validating) {
std::vector<char> buffer(size); std::vector<char> buffer(size);
lseek64(device_fd, sector * kSectorSize, SEEK_SET); off64_t offset = sector * kSectorSize;
read(device_fd, &buffer[0], size); if (lseek64(device_fd, offset, SEEK_SET) != offset) {
return std::vector<char>();
}
if (read(device_fd, &buffer[0], size) != static_cast<ssize_t>(size)) {
return std::vector<char>();
}
return buffer; return buffer;
} }
std::vector<char> buffer(size); std::vector<char> buffer(size);
for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) { for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) {
auto relocation = --relocations.upper_bound(sector); auto relocation = --relocations.upper_bound(sector);
lseek64(device_fd, (sector + relocation->second - relocation->first) * kSectorSize, off64_t offset = (sector + relocation->second - relocation->first) * kSectorSize;
SEEK_SET); if (lseek64(device_fd, offset, SEEK_SET) != offset) {
read(device_fd, &buffer[i], block_size); return std::vector<char>();
}
if (read(device_fd, &buffer[i], block_size) != static_cast<ssize_t>(block_size)) {
return std::vector<char>();
}
} }
return buffer; return buffer;
@ -619,7 +644,10 @@ Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
if (device_fd < 0) return error("Cannot open " + blockDevice); if (device_fd < 0) return error("Cannot open " + blockDevice);
log_sector_v1_0 original_ls; log_sector_v1_0 original_ls;
read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls)); if (read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls)) !=
sizeof(original_ls)) {
return error(EINVAL, "Cannot read sector");
}
if (original_ls.magic == kPartialRestoreMagic) { if (original_ls.magic == kPartialRestoreMagic) {
validating = false; validating = false;
action = "Restoring"; action = "Restoring";
@ -627,11 +655,19 @@ Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
return error(EINVAL, "No magic"); return error(EINVAL, "No magic");
} }
if (original_ls.block_size < sizeof(log_sector_v1_0)) {
return error(EINVAL, "Block size is invalid");
}
LOG(INFO) << action << " " << original_ls.sequence << " log sectors"; LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) { for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0, auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0,
original_ls.block_size, original_ls.block_size); original_ls.block_size, original_ls.block_size);
if (ls_buffer.size() != original_ls.block_size) {
status = error(EINVAL, "Failed to read log sector");
break;
}
log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]); log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
Used_Sectors used_sectors; Used_Sectors used_sectors;
@ -653,6 +689,14 @@ Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
break; break;
} }
if (ls.header_size < sizeof(log_sector_v1_0) || ls.header_size > ls.block_size) {
status = error(EINVAL, "Log sector header size is invalid");
break;
}
if (ls.count < 1 || ls.count > (ls.block_size - ls.header_size) / sizeof(log_entry)) {
status = error(EINVAL, "Log sector count is invalid");
break;
}
LOG(INFO) << action << " from log sector " << ls.sequence; LOG(INFO) << action << " from log sector " << ls.sequence;
for (log_entry* le = for (log_entry* le =
reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1; reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1;
@ -662,8 +706,16 @@ Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
<< " to " << le->source << " with checksum " << std::hex << " to " << le->source << " with checksum " << std::hex
<< le->checksum; << le->checksum;
if (ls.block_size > UINT_MAX - le->size || le->size < ls.block_size) {
status = error(EINVAL, "log entry is invalid");
break;
}
auto buffer = relocatedRead(device_fd, relocations, validating, le->dest, le->size, auto buffer = relocatedRead(device_fd, relocations, validating, le->dest, le->size,
ls.block_size); ls.block_size);
if (buffer.size() != le->size) {
status = error(EINVAL, "Failed to read sector");
break;
}
uint32_t checksum = le->source / (ls.block_size / kSectorSize); uint32_t checksum = le->source / (ls.block_size / kSectorSize);
for (size_t i = 0; i < le->size; i += ls.block_size) { for (size_t i = 0; i < le->size; i += ls.block_size) {
crc32(&buffer[i], ls.block_size, &checksum); crc32(&buffer[i], ls.block_size, &checksum);
@ -696,8 +748,17 @@ Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward"; LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0, auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0,
original_ls.block_size, original_ls.block_size); original_ls.block_size, original_ls.block_size);
lseek64(device_fd, 0, SEEK_SET); if (buffer.size() != original_ls.block_size) {
write(device_fd, &buffer[0], original_ls.block_size); return error(EINVAL, "Failed to read original sector");
}
if (lseek64(device_fd, 0, SEEK_SET) != 0) {
return error(EINVAL, "Failed to seek to sector 0");
}
if (write(device_fd, &buffer[0], original_ls.block_size) !=
static_cast<ssize_t>(original_ls.block_size)) {
return error(EINVAL, "Failed to write original sector");
}
return Status::ok(); return Status::ok();
} }

View file

@ -16,6 +16,7 @@
#include "FsCrypt.h" #include "FsCrypt.h"
#include "Checkpoint.h"
#include "KeyStorage.h" #include "KeyStorage.h"
#include "KeyUtil.h" #include "KeyUtil.h"
#include "Utils.h" #include "Utils.h"
@ -96,9 +97,15 @@ const std::string data_data_dir = std::string() + DATA_MNT_POINT + "/data";
const std::string data_user_0_dir = std::string() + DATA_MNT_POINT + "/user/0"; const std::string data_user_0_dir = std::string() + DATA_MNT_POINT + "/user/0";
const std::string media_obb_dir = std::string() + DATA_MNT_POINT + "/media/obb"; const std::string media_obb_dir = std::string() + DATA_MNT_POINT + "/media/obb";
// Some users are ephemeral, don't try to wipe their keys from disk // The file encryption options to use on the /data filesystem
EncryptionOptions s_data_options;
// Some users are ephemeral; don't try to store or wipe their keys on disk.
std::set<userid_t> s_ephemeral_users; std::set<userid_t> s_ephemeral_users;
// New CE keys that haven't been committed to disk yet
std::map<userid_t, KeyBuffer> s_new_ce_keys;
// The system DE encryption policy // The system DE encryption policy
EncryptionPolicy s_device_policy; EncryptionPolicy s_device_policy;
@ -106,10 +113,17 @@ EncryptionPolicy s_device_policy;
std::map<userid_t, EncryptionPolicy> s_de_policies; std::map<userid_t, EncryptionPolicy> s_de_policies;
std::map<userid_t, EncryptionPolicy> s_ce_policies; std::map<userid_t, EncryptionPolicy> s_ce_policies;
// CE key fixation operations that have been deferred to checkpoint commit
std::map<std::string, std::string> s_deferred_fixations;
} // namespace } // namespace
// Returns KeyGeneration suitable for key as described in EncryptionOptions // Returns KeyGeneration suitable for key as described in EncryptionOptions
static KeyGeneration makeGen(const EncryptionOptions& options) { static KeyGeneration makeGen(const EncryptionOptions& options) {
if (options.version == 0) {
LOG(ERROR) << "EncryptionOptions not initialized";
return android::vold::neverGen();
}
return KeyGeneration{FSCRYPT_MAX_KEY_SIZE, true, options.use_hw_wrapped_key}; return KeyGeneration{FSCRYPT_MAX_KEY_SIZE, true, options.use_hw_wrapped_key};
} }
@ -176,20 +190,23 @@ static bool get_ce_key_new_path(const std::string& directory_path,
} }
// Discard all keys but the named one; rename it to canonical name. // Discard all keys but the named one; rename it to canonical name.
// No point in acting on errors in this; ignore them. static bool fixate_user_ce_key(const std::string& directory_path, const std::string& to_fix,
static void fixate_user_ce_key(const std::string& directory_path, const std::string& to_fix,
const std::vector<std::string>& paths) { const std::vector<std::string>& paths) {
bool need_sync = false;
for (auto const other_path : paths) { for (auto const other_path : paths) {
if (other_path != to_fix) { if (other_path != to_fix) {
android::vold::destroyKey(other_path); android::vold::destroyKey(other_path);
need_sync = true;
} }
} }
auto const current_path = get_ce_key_current_path(directory_path); auto const current_path = get_ce_key_current_path(directory_path);
if (to_fix != current_path) { if (to_fix != current_path) {
LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path; LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path;
if (!android::vold::RenameKeyDir(to_fix, current_path)) return; if (!android::vold::RenameKeyDir(to_fix, current_path)) return false;
need_sync = true;
} }
android::vold::FsyncDirectory(directory_path); if (need_sync && !android::vold::FsyncDirectory(directory_path)) return false;
return true;
} }
static bool read_and_fixate_user_ce_key(userid_t user_id, static bool read_and_fixate_user_ce_key(userid_t user_id,
@ -201,6 +218,7 @@ static bool read_and_fixate_user_ce_key(userid_t user_id,
LOG(DEBUG) << "Trying user CE key " << ce_key_path; LOG(DEBUG) << "Trying user CE key " << ce_key_path;
if (retrieveKey(ce_key_path, auth, ce_key)) { if (retrieveKey(ce_key_path, auth, ce_key)) {
LOG(DEBUG) << "Successfully retrieved key"; LOG(DEBUG) << "Successfully retrieved key";
s_deferred_fixations.erase(directory_path);
fixate_user_ce_key(directory_path, ce_key_path, paths); fixate_user_ce_key(directory_path, ce_key_path, paths);
return true; return true;
} }
@ -236,19 +254,19 @@ static bool MightBeEmmcStorage(const std::string& blk_device) {
StartsWith(name, "vd"); StartsWith(name, "vd");
} }
// Retrieve the options to use for encryption policies on the /data filesystem. // Sets s_data_options to the file encryption options for the /data filesystem.
static bool get_data_file_encryption_options(EncryptionOptions* options) { static bool init_data_file_encryption_options() {
auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT); auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
if (entry == nullptr) { if (entry == nullptr) {
LOG(ERROR) << "No mount point entry for " << DATA_MNT_POINT; LOG(ERROR) << "No mount point entry for " << DATA_MNT_POINT;
return false; return false;
} }
if (!ParseOptions(entry->encryption_options, options)) { if (!ParseOptions(entry->encryption_options, &s_data_options)) {
LOG(ERROR) << "Unable to parse encryption options for " << DATA_MNT_POINT ": " LOG(ERROR) << "Unable to parse encryption options for " << DATA_MNT_POINT ": "
<< entry->encryption_options; << entry->encryption_options;
return false; return false;
} }
if ((options->flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) && if ((s_data_options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) &&
!MightBeEmmcStorage(entry->blk_device)) { !MightBeEmmcStorage(entry->blk_device)) {
LOG(ERROR) << "The emmc_optimized encryption flag is only allowed on eMMC storage. Remove " LOG(ERROR) << "The emmc_optimized encryption flag is only allowed on eMMC storage. Remove "
"this flag from the device's fstab"; "this flag from the device's fstab";
@ -259,6 +277,10 @@ static bool get_data_file_encryption_options(EncryptionOptions* options) {
static bool install_storage_key(const std::string& mountpoint, const EncryptionOptions& options, static bool install_storage_key(const std::string& mountpoint, const EncryptionOptions& options,
const KeyBuffer& key, EncryptionPolicy* policy) { const KeyBuffer& key, EncryptionPolicy* policy) {
if (options.version == 0) {
LOG(ERROR) << "EncryptionOptions not initialized";
return false;
}
KeyBuffer ephemeral_wrapped_key; KeyBuffer ephemeral_wrapped_key;
if (options.use_hw_wrapped_key) { if (options.use_hw_wrapped_key) {
if (!exportWrappedStorageKey(key, &ephemeral_wrapped_key)) { if (!exportWrappedStorageKey(key, &ephemeral_wrapped_key)) {
@ -297,12 +319,10 @@ static bool get_volume_file_encryption_options(EncryptionOptions* options) {
static bool read_and_install_user_ce_key(userid_t user_id, static bool read_and_install_user_ce_key(userid_t user_id,
const android::vold::KeyAuthentication& auth) { const android::vold::KeyAuthentication& auth) {
if (s_ce_policies.count(user_id) != 0) return true; if (s_ce_policies.count(user_id) != 0) return true;
EncryptionOptions options;
if (!get_data_file_encryption_options(&options)) return false;
KeyBuffer ce_key; KeyBuffer ce_key;
if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false; if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
EncryptionPolicy ce_policy; EncryptionPolicy ce_policy;
if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false; if (!install_storage_key(DATA_MNT_POINT, s_data_options, ce_key, &ce_policy)) return false;
s_ce_policies[user_id] = ce_policy; s_ce_policies[user_id] = ce_policy;
LOG(DEBUG) << "Installed ce key for user " << user_id; LOG(DEBUG) << "Installed ce key for user " << user_id;
return true; return true;
@ -366,39 +386,50 @@ static bool destroy_dir(const std::string& dir) {
return true; return true;
} }
// NB this assumes that there is only one thread listening for crypt commands, because // Checks whether the DE key directory exists for the given user.
// it creates keys in a fixed location. static bool de_key_exists(userid_t user_id) {
static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) { return android::vold::pathExists(get_de_key_path(user_id));
EncryptionOptions options; }
if (!get_data_file_encryption_options(&options)) return false;
KeyBuffer de_key, ce_key; // Checks whether at least one CE key subdirectory exists for the given user.
if (!generateStorageKey(makeGen(options), &de_key)) return false; static bool ce_key_exists(userid_t user_id) {
if (!generateStorageKey(makeGen(options), &ce_key)) return false; auto directory_path = get_ce_key_directory_path(user_id);
if (create_ephemeral) { // The common case is that "$dir/current" exists, so check for that first.
// If the key should be created as ephemeral, don't store it. if (android::vold::pathExists(get_ce_key_current_path(directory_path))) return true;
s_ephemeral_users.insert(user_id);
} else { // Else, there could still be another subdirectory of $dir (if a crash
auto const directory_path = get_ce_key_directory_path(user_id); // occurred during fixate_user_ce_key()), so check for one.
if (!prepare_dir(directory_path, 0700, AID_ROOT, AID_ROOT)) return false; return android::vold::pathExists(directory_path) && !get_ce_key_paths(directory_path).empty();
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; static bool create_de_key(userid_t user_id, bool ephemeral) {
if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, kEmptyAuthentication, KeyBuffer de_key;
ce_key)) if (!generateStorageKey(makeGen(s_data_options), &de_key)) return false;
return false; if (!ephemeral && !android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp,
fixate_user_ce_key(directory_path, ce_key_path, paths);
// Write DE key second; once this is written, all is good.
if (!android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp,
kEmptyAuthentication, de_key)) kEmptyAuthentication, de_key))
return false; return false;
}
EncryptionPolicy de_policy; EncryptionPolicy de_policy;
if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false; if (!install_storage_key(DATA_MNT_POINT, s_data_options, de_key, &de_policy)) return false;
s_de_policies[user_id] = de_policy; s_de_policies[user_id] = de_policy;
LOG(INFO) << "Created DE key for user " << user_id;
return true;
}
static bool create_ce_key(userid_t user_id, bool ephemeral) {
KeyBuffer ce_key;
if (!generateStorageKey(makeGen(s_data_options), &ce_key)) return false;
if (!ephemeral) {
if (!prepare_dir(get_ce_key_directory_path(user_id), 0700, AID_ROOT, AID_ROOT))
return false;
// We don't store the CE key on disk here, since here we don't have the
// secret needed to do so securely. Instead, we cache it in memory for
// now, and we store it later in fscrypt_set_user_key_protection().
s_new_ce_keys.insert({user_id, ce_key});
}
EncryptionPolicy ce_policy; EncryptionPolicy ce_policy;
if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false; if (!install_storage_key(DATA_MNT_POINT, s_data_options, ce_key, &ce_policy)) return false;
s_ce_policies[user_id] = ce_policy; s_ce_policies[user_id] = ce_policy;
LOG(DEBUG) << "Created keys for user " << user_id; LOG(INFO) << "Created CE key for user " << user_id;
return true; return true;
} }
@ -420,8 +451,6 @@ static bool is_numeric(const char* name) {
} }
static bool load_all_de_keys() { static bool load_all_de_keys() {
EncryptionOptions options;
if (!get_data_file_encryption_options(&options)) return false;
auto de_dir = user_key_dir + "/de"; auto de_dir = user_key_dir + "/de";
auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir); auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir);
if (!dirp) { if (!dirp) {
@ -452,7 +481,7 @@ static bool load_all_de_keys() {
return false; return false;
} }
EncryptionPolicy de_policy; EncryptionPolicy de_policy;
if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false; if (!install_storage_key(DATA_MNT_POINT, s_data_options, de_key, &de_policy)) return false;
auto ret = s_de_policies.insert({user_id, de_policy}); auto ret = s_de_policies.insert({user_id, de_policy});
if (!ret.second && ret.first->second != de_policy) { if (!ret.second && ret.first->second != de_policy) {
LOG(ERROR) << "DE policy for user" << user_id << " changed"; LOG(ERROR) << "DE policy for user" << user_id << " changed";
@ -479,17 +508,17 @@ static bool try_reload_ce_keys() {
bool fscrypt_initialize_systemwide_keys() { bool fscrypt_initialize_systemwide_keys() {
LOG(INFO) << "fscrypt_initialize_systemwide_keys"; LOG(INFO) << "fscrypt_initialize_systemwide_keys";
EncryptionOptions options; if (!init_data_file_encryption_options()) return false;
if (!get_data_file_encryption_options(&options)) return false;
KeyBuffer device_key; KeyBuffer device_key;
if (!retrieveOrGenerateKey(device_key_path, device_key_temp, kEmptyAuthentication, if (!retrieveOrGenerateKey(device_key_path, device_key_temp, kEmptyAuthentication,
makeGen(options), &device_key)) makeGen(s_data_options), &device_key))
return false; return false;
// This initializes s_device_policy, which is a global variable so that // This initializes s_device_policy, which is a global variable so that
// fscrypt_init_user0() can access it later. // fscrypt_init_user0() can access it later.
if (!install_storage_key(DATA_MNT_POINT, options, device_key, &s_device_policy)) return false; if (!install_storage_key(DATA_MNT_POINT, s_data_options, device_key, &s_device_policy))
return false;
std::string options_string; std::string options_string;
if (!OptionsToString(s_device_policy.options, &options_string)) { if (!OptionsToString(s_device_policy.options, &options_string)) {
@ -504,9 +533,10 @@ bool fscrypt_initialize_systemwide_keys() {
LOG(INFO) << "Wrote system DE key reference to:" << ref_filename; LOG(INFO) << "Wrote system DE key reference to:" << ref_filename;
KeyBuffer per_boot_key; KeyBuffer per_boot_key;
if (!generateStorageKey(makeGen(options), &per_boot_key)) return false; if (!generateStorageKey(makeGen(s_data_options), &per_boot_key)) return false;
EncryptionPolicy per_boot_policy; EncryptionPolicy per_boot_policy;
if (!install_storage_key(DATA_MNT_POINT, options, per_boot_key, &per_boot_policy)) return false; if (!install_storage_key(DATA_MNT_POINT, s_data_options, per_boot_key, &per_boot_policy))
return false;
std::string per_boot_ref_filename = std::string("/data") + fscrypt_key_per_boot_ref; std::string per_boot_ref_filename = std::string("/data") + fscrypt_key_per_boot_ref;
if (!android::vold::writeStringToFile(per_boot_policy.key_raw_ref, per_boot_ref_filename)) if (!android::vold::writeStringToFile(per_boot_policy.key_raw_ref, per_boot_ref_filename))
return false; return false;
@ -533,8 +563,17 @@ static bool prepare_special_dirs() {
// opportunity to also set the encryption policy of /data/data right away. // opportunity to also set the encryption policy of /data/data right away.
EncryptionPolicy ce_policy; EncryptionPolicy ce_policy;
if (lookup_policy(s_ce_policies, 0, &ce_policy)) { if (lookup_policy(s_ce_policies, 0, &ce_policy)) {
if (!prepare_dir_with_policy(data_data_dir, 0771, AID_SYSTEM, AID_SYSTEM, ce_policy)) {
// Preparing /data/data failed, yet we had just generated a new CE
// key because one wasn't stored. Before erroring out, try deleting
// the directory and retrying, as it's possible that the directory
// exists with different CE policy from an interrupted first boot.
if (rmdir(data_data_dir.c_str()) != 0) {
PLOG(ERROR) << "rmdir " << data_data_dir << " failed";
}
if (!prepare_dir_with_policy(data_data_dir, 0771, AID_SYSTEM, AID_SYSTEM, ce_policy)) if (!prepare_dir_with_policy(data_data_dir, 0771, AID_SYSTEM, AID_SYSTEM, ce_policy))
return false; return false;
}
} else { } else {
if (!prepare_dir(data_data_dir, 0771, AID_SYSTEM, AID_SYSTEM)) return false; if (!prepare_dir(data_data_dir, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
// EnsurePolicy() will have to happen later, in fscrypt_prepare_user_storage(). // EnsurePolicy() will have to happen later, in fscrypt_prepare_user_storage().
@ -570,9 +609,13 @@ bool fscrypt_init_user0() {
if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false; 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 + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false; if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
if (!android::vold::pathExists(get_de_key_path(0))) {
if (!create_and_install_user_keys(0, false)) return false; // Create user 0's DE and CE keys if they don't already exist. Check
} // each key independently, since if the first boot was interrupted it is
// possible that the DE key exists but the CE key does not.
if (!de_key_exists(0) && !create_de_key(0, false)) return false;
if (!ce_key_exists(0) && !create_ce_key(0, false)) return false;
// TODO: switch to loading only DE_0 here once framework makes // TODO: switch to loading only DE_0 here once framework makes
// explicit calls to install DE keys for secondary users // explicit calls to install DE keys for secondary users
if (!load_all_de_keys()) return false; if (!load_all_de_keys()) return false;
@ -613,9 +656,9 @@ bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral)
// FIXME should we fail the command? // FIXME should we fail the command?
return true; return true;
} }
if (!create_and_install_user_keys(user_id, ephemeral)) { if (!create_de_key(user_id, ephemeral)) return false;
return false; if (!create_ce_key(user_id, ephemeral)) return false;
} if (ephemeral) s_ephemeral_users.insert(user_id);
return true; return true;
} }
@ -649,6 +692,7 @@ static bool evict_ce_key(userid_t user_id) {
drop_caches_if_needed(); drop_caches_if_needed();
} }
s_ce_policies.erase(user_id); s_ce_policies.erase(user_id);
s_new_ce_keys.erase(user_id);
return success; return success;
} }
@ -663,14 +707,14 @@ bool fscrypt_destroy_user_key(userid_t user_id) {
success &= lookup_policy(s_de_policies, user_id, &de_policy) && success &= lookup_policy(s_de_policies, user_id, &de_policy) &&
android::vold::evictKey(DATA_MNT_POINT, de_policy); android::vold::evictKey(DATA_MNT_POINT, de_policy);
s_de_policies.erase(user_id); s_de_policies.erase(user_id);
auto it = s_ephemeral_users.find(user_id); if (!s_ephemeral_users.erase(user_id)) {
if (it != s_ephemeral_users.end()) {
s_ephemeral_users.erase(it);
} else {
auto ce_path = get_ce_key_directory_path(user_id); auto ce_path = get_ce_key_directory_path(user_id);
if (!s_new_ce_keys.erase(user_id)) {
for (auto const path : get_ce_key_paths(ce_path)) { for (auto const path : get_ce_key_paths(ce_path)) {
success &= android::vold::destroyKey(path); success &= android::vold::destroyKey(path);
} }
}
s_deferred_fixations.erase(ce_path);
success &= destroy_dir(ce_path); success &= destroy_dir(ce_path);
auto de_key_path = get_de_key_path(user_id); auto de_key_path = get_de_key_path(user_id);
@ -745,60 +789,100 @@ static bool destroy_volkey(const std::string& misc_path, const std::string& volu
return android::vold::destroyKey(path); return android::vold::destroyKey(path);
} }
static bool fscrypt_rewrap_user_key(userid_t user_id, int serial, // (Re-)encrypts the user's CE key with the given secret. This function handles
const android::vold::KeyAuthentication& retrieve_auth, // storing the CE key for a new user for the first time. It also handles
const android::vold::KeyAuthentication& store_auth) { // re-encrypting the CE key upon upgrade from an Android version where the CE
if (s_ephemeral_users.count(user_id) != 0) return true; // key was stored with kEmptyAuthentication when the user didn't have an LSKF.
auto const directory_path = get_ce_key_directory_path(user_id); // See the comments below for the different cases handled.
KeyBuffer ce_key; bool fscrypt_set_user_key_protection(userid_t user_id, const std::string& secret_hex) {
std::string ce_key_current_path = get_ce_key_current_path(directory_path); LOG(DEBUG) << "fscrypt_set_user_key_protection " << user_id;
if (retrieveKey(ce_key_current_path, retrieve_auth, &ce_key)) { if (!IsFbeEnabled()) return true;
LOG(DEBUG) << "Successfully retrieved key"; auto auth = authentication_from_hex(secret_hex);
// TODO(147732812): Remove this once Locksettingservice is fixed. if (!auth) return false;
// Currently it calls fscrypt_clear_user_key_auth with a secret when lockscreen is if (auth->secret.empty()) {
// changed from swipe to none or vice-versa LOG(ERROR) << "fscrypt_set_user_key_protection: secret must be nonempty";
} else if (retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key)) {
LOG(DEBUG) << "Successfully retrieved key with empty auth";
} else {
LOG(ERROR) << "Failed to retrieve key for user " << user_id;
return false; return false;
} }
// We shouldn't store any keys for ephemeral users.
if (s_ephemeral_users.count(user_id) != 0) {
LOG(DEBUG) << "Not storing key because user is ephemeral";
return true;
}
KeyBuffer ce_key;
auto it = s_new_ce_keys.find(user_id);
if (it != s_new_ce_keys.end()) {
// If the key exists in s_new_ce_keys, then the key is a
// not-yet-committed key for a new user, and we are committing it here.
// This happens when the user's synthetic password is created.
ce_key = it->second;
} else if (ce_key_exists(user_id)) {
// If the key doesn't exist in s_new_ce_keys but does exist on-disk,
// then we are setting the protection on an existing key. This happens
// at upgrade time, when CE keys that were previously protected by
// kEmptyAuthentication are encrypted by the user's synthetic password.
LOG(DEBUG) << "CE key already exists on-disk; re-protecting it with the given secret";
if (!read_and_fixate_user_ce_key(user_id, kEmptyAuthentication, &ce_key)) {
LOG(ERROR) << "Failed to retrieve CE key for user " << user_id << " using empty auth";
// Before failing, also check whether the key is already protected
// with the given secret. This isn't expected, but in theory it
// could happen if an upgrade is requested for a user more than once
// due to a power-off or other interruption.
if (read_and_fixate_user_ce_key(user_id, *auth, &ce_key)) {
LOG(WARNING) << "CE key is already protected by given secret";
return true;
}
// The key isn't protected by either kEmptyAuthentication or by
// |auth|. This should never happen, and there's nothing we can do
// besides return an error.
return false;
}
} else {
// If the key doesn't exist in memory or on-disk, then we need to
// generate it here, then commit it to disk. This is needed after the
// unusual case where a non-system user was created during early boot,
// and then the device was force-rebooted before the boot completed. In
// that case, the Android user record was committed but the CE key was
// not. So the CE key was lost, and we need to regenerate it. This
// should be fine, since the key should not have been used yet.
LOG(WARNING) << "CE key not found! Regenerating it";
if (!create_ce_key(user_id, false)) return false;
ce_key = s_new_ce_keys.find(user_id)->second;
}
auto const directory_path = get_ce_key_directory_path(user_id);
auto const paths = get_ce_key_paths(directory_path); auto const paths = get_ce_key_paths(directory_path);
std::string ce_key_path; std::string ce_key_path;
if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false; if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, store_auth, ce_key)) if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, *auth, ce_key)) return false;
return false;
// Fixate the key, i.e. delete all other bindings of it. (In practice this
// just means the kEmptyAuthentication binding, if there is one.) However,
// if a userdata filesystem checkpoint is pending, then we need to delay the
// fixation until the checkpoint has been committed, since deleting keys
// from Keystore cannot be rolled back.
if (android::vold::cp_needsCheckpoint()) {
LOG(INFO) << "Deferring fixation of " << directory_path << " until checkpoint is committed";
s_deferred_fixations[directory_path] = ce_key_path;
} else {
s_deferred_fixations.erase(directory_path);
if (!fixate_user_ce_key(directory_path, ce_key_path, paths)) return false;
}
if (s_new_ce_keys.erase(user_id)) {
LOG(INFO) << "Stored CE key for new user " << user_id;
}
return true; return true;
} }
bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& secret_hex) { void fscrypt_deferred_fixate_ce_keys() {
LOG(DEBUG) << "fscrypt_add_user_key_auth " << user_id << " serial=" << serial; for (const auto& it : s_deferred_fixations) {
if (!IsFbeEnabled()) return true; const auto& directory_path = it.first;
auto auth = authentication_from_hex(secret_hex); const auto& to_fix = it.second;
if (!auth) return false; LOG(INFO) << "Doing deferred fixation of " << directory_path;
return fscrypt_rewrap_user_key(user_id, serial, kEmptyAuthentication, *auth); fixate_user_ce_key(directory_path, to_fix, get_ce_key_paths(directory_path));
// Continue on error.
} }
s_deferred_fixations.clear();
bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& secret_hex) {
LOG(DEBUG) << "fscrypt_clear_user_key_auth " << user_id << " serial=" << serial;
if (!IsFbeEnabled()) return true;
auto auth = authentication_from_hex(secret_hex);
if (!auth) return false;
return fscrypt_rewrap_user_key(user_id, serial, *auth, kEmptyAuthentication);
}
bool fscrypt_fixate_newest_user_key_auth(userid_t user_id) {
LOG(DEBUG) << "fscrypt_fixate_newest_user_key_auth " << user_id;
if (!IsFbeEnabled()) return true;
if (s_ephemeral_users.count(user_id) != 0) return true;
auto const directory_path = get_ce_key_directory_path(user_id);
auto const paths = get_ce_key_paths(directory_path);
if (paths.empty()) {
LOG(ERROR) << "No ce keys present, cannot fixate for user " << user_id;
return false;
}
fixate_user_ce_key(directory_path, paths[0], paths);
return true;
} }
std::vector<int> fscrypt_get_unlocked_users() { std::vector<int> fscrypt_get_unlocked_users() {

View file

@ -25,9 +25,8 @@ bool fscrypt_init_user0();
extern bool fscrypt_init_user0_done; extern bool fscrypt_init_user0_done;
bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral); bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
bool fscrypt_destroy_user_key(userid_t user_id); bool fscrypt_destroy_user_key(userid_t user_id);
bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& secret); bool fscrypt_set_user_key_protection(userid_t user_id, const std::string& secret);
bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& secret); void fscrypt_deferred_fixate_ce_keys();
bool fscrypt_fixate_newest_user_key_auth(userid_t user_id);
std::vector<int> fscrypt_get_unlocked_users(); std::vector<int> fscrypt_get_unlocked_users();
bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& secret); bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& secret);

View file

@ -23,7 +23,6 @@
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <thread>
#include <vector> #include <vector>
#include <errno.h> #include <errno.h>
@ -231,9 +230,8 @@ static bool CommitUpgradedKey(Keystore& keystore, const std::string& dir) {
return true; return true;
} }
static void DeferredCommitKeys() { void DeferredCommitKeystoreKeys() {
android::base::WaitForProperty("vold.checkpoint_committed", "1"); LOG(INFO) << "Committing upgraded Keystore keys";
LOG(INFO) << "Committing upgraded keys";
Keystore keystore; Keystore keystore;
if (!keystore) { if (!keystore) {
LOG(ERROR) << "Failed to open Keystore; old keys won't be deleted from Keystore"; LOG(ERROR) << "Failed to open Keystore; old keys won't be deleted from Keystore";
@ -241,10 +239,11 @@ static void DeferredCommitKeys() {
} }
std::lock_guard<std::mutex> lock(key_upgrade_lock); std::lock_guard<std::mutex> lock(key_upgrade_lock);
for (auto& dir : key_dirs_to_commit) { for (auto& dir : key_dirs_to_commit) {
LOG(INFO) << "Committing upgraded key " << dir; LOG(INFO) << "Committing upgraded Keystore key for " << dir;
CommitUpgradedKey(keystore, dir); CommitUpgradedKey(keystore, dir);
} }
key_dirs_to_commit.clear(); key_dirs_to_commit.clear();
LOG(INFO) << "Done committing upgraded Keystore keys";
} }
// Returns true if the Keystore key in |dir| has already been upgraded and is // Returns true if the Keystore key in |dir| has already been upgraded and is
@ -260,7 +259,6 @@ static bool IsKeyCommitPending(const std::string& dir) {
// that key_upgrade_lock is held and that a commit isn't already pending for the // that key_upgrade_lock is held and that a commit isn't already pending for the
// directory. // directory.
static void ScheduleKeyCommit(const std::string& dir) { static void ScheduleKeyCommit(const std::string& dir) {
if (key_dirs_to_commit.empty()) std::thread(DeferredCommitKeys).detach();
key_dirs_to_commit.push_back(dir); key_dirs_to_commit.push_back(dir);
} }
@ -592,7 +590,7 @@ bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path
if (!RenameKeyDir(tmp_path, key_path)) return false; if (!RenameKeyDir(tmp_path, key_path)) return false;
if (!FsyncParentDirectory(key_path)) return false; if (!FsyncParentDirectory(key_path)) return false;
LOG(DEBUG) << "Created key: " << key_path; LOG(DEBUG) << "Stored key " << key_path;
return true; return true;
} }

View file

@ -41,6 +41,8 @@ extern const KeyAuthentication kEmptyAuthentication;
bool createSecdiscardable(const std::string& path, std::string* hash); bool createSecdiscardable(const std::string& path, std::string* hash);
bool readSecdiscardable(const std::string& path, std::string* hash); bool readSecdiscardable(const std::string& path, std::string* hash);
void DeferredCommitKeystoreKeys();
// Renames a key directory while also managing deferred commits appropriately. // Renames a key directory while also managing deferred commits appropriately.
// This method should be used whenever a key directory needs to be moved/renamed. // This method should be used whenever a key directory needs to be moved/renamed.
bool RenameKeyDir(const std::string& old_name, const std::string& new_name); bool RenameKeyDir(const std::string& old_name, const std::string& new_name);

View file

@ -1771,24 +1771,45 @@ std::pair<android::base::unique_fd, std::string> OpenDirInProcfs(std::string_vie
return {std::move(fd), std::move(linkPath)}; return {std::move(fd), std::move(linkPath)};
} }
bool IsFuseBpfEnabled() { static bool IsPropertySet(const char* name, bool& value) {
// TODO Once kernel supports flag, trigger off kernel flag unless if (base::GetProperty(name, "") == "") return false;
// ro.fuse.bpf.enabled is explicitly set to false
bool enabled;
if (base::GetProperty("ro.fuse.bpf.is_running", "") != "")
enabled = base::GetBoolProperty("ro.fuse.bpf.is_running", false);
else if (base::GetProperty("persist.sys.fuse.bpf.override", "") != "")
enabled = base::GetBoolProperty("persist.sys.fuse.bpf.override", false);
else
enabled = base::GetBoolProperty("ro.fuse.bpf.enabled", false);
if (enabled) { value = base::GetBoolProperty(name, false);
base::SetProperty("ro.fuse.bpf.is_running", "true"); LOG(INFO) << "fuse-bpf is " << (value ? "enabled" : "disabled") << " because of property "
<< name;
return true; return true;
} else {
base::SetProperty("ro.fuse.bpf.is_running", "false");
return false;
} }
bool IsFuseBpfEnabled() {
// This logic is reproduced in packages/providers/MediaProvider/jni/FuseDaemon.cpp
// so changes made here must be reflected there
bool enabled = false;
if (IsPropertySet("ro.fuse.bpf.is_running", enabled)) return enabled;
if (!IsPropertySet("persist.sys.fuse.bpf.override", enabled) &&
!IsPropertySet("ro.fuse.bpf.enabled", enabled)) {
// If the kernel has fuse-bpf, /sys/fs/fuse/features/fuse_bpf will exist and have the
// contents 'supported\n' - see fs/fuse/inode.c in the kernel source
std::string contents;
const char* filename = "/sys/fs/fuse/features/fuse_bpf";
if (!base::ReadFileToString(filename, &contents)) {
LOG(INFO) << "fuse-bpf is disabled because " << filename << " cannot be read";
enabled = false;
} else if (contents == "supported\n") {
LOG(INFO) << "fuse-bpf is enabled because " << filename << " reads 'supported'";
enabled = true;
} else {
LOG(INFO) << "fuse-bpf is disabled because " << filename
<< " does not read 'supported'";
enabled = false;
}
}
std::string value = enabled ? "true" : "false";
LOG(INFO) << "Setting ro.fuse.bpf.is_running to " << value;
base::SetProperty("ro.fuse.bpf.is_running", value);
return enabled;
} }
} // namespace vold } // namespace vold

View file

@ -612,27 +612,11 @@ binder::Status VoldNativeService::destroyUserKey(int32_t userId) {
return translateBool(fscrypt_destroy_user_key(userId)); return translateBool(fscrypt_destroy_user_key(userId));
} }
binder::Status VoldNativeService::addUserKeyAuth(int32_t userId, int32_t userSerial, binder::Status VoldNativeService::setUserKeyProtection(int32_t userId, const std::string& secret) {
const std::string& secret) {
ENFORCE_SYSTEM_OR_ROOT; ENFORCE_SYSTEM_OR_ROOT;
ACQUIRE_CRYPT_LOCK; ACQUIRE_CRYPT_LOCK;
return translateBool(fscrypt_add_user_key_auth(userId, userSerial, secret)); return translateBool(fscrypt_set_user_key_protection(userId, secret));
}
binder::Status VoldNativeService::clearUserKeyAuth(int32_t userId, int32_t userSerial,
const std::string& secret) {
ENFORCE_SYSTEM_OR_ROOT;
ACQUIRE_CRYPT_LOCK;
return translateBool(fscrypt_clear_user_key_auth(userId, userSerial, secret));
}
binder::Status VoldNativeService::fixateNewestUserKeyAuth(int32_t userId) {
ENFORCE_SYSTEM_OR_ROOT;
ACQUIRE_CRYPT_LOCK;
return translateBool(fscrypt_fixate_newest_user_key_auth(userId));
} }
binder::Status VoldNativeService::getUnlockedUsers(std::vector<int>* _aidl_return) { binder::Status VoldNativeService::getUnlockedUsers(std::vector<int>* _aidl_return) {

View file

@ -115,9 +115,7 @@ class VoldNativeService : public BinderService<VoldNativeService>, public os::Bn
binder::Status createUserKey(int32_t userId, int32_t userSerial, bool ephemeral); binder::Status createUserKey(int32_t userId, int32_t userSerial, bool ephemeral);
binder::Status destroyUserKey(int32_t userId); binder::Status destroyUserKey(int32_t userId);
binder::Status addUserKeyAuth(int32_t userId, int32_t userSerial, const std::string& secret); binder::Status setUserKeyProtection(int32_t userId, const std::string& secret);
binder::Status clearUserKeyAuth(int32_t userId, int32_t userSerial, const std::string& secret);
binder::Status fixateNewestUserKeyAuth(int32_t userId);
binder::Status getUnlockedUsers(std::vector<int>* _aidl_return); binder::Status getUnlockedUsers(std::vector<int>* _aidl_return);
binder::Status unlockUserKey(int32_t userId, int32_t userSerial, const std::string& secret); binder::Status unlockUserKey(int32_t userId, int32_t userSerial, const std::string& secret);

View file

@ -88,9 +88,7 @@ interface IVold {
void createUserKey(int userId, int userSerial, boolean ephemeral); void createUserKey(int userId, int userSerial, boolean ephemeral);
void destroyUserKey(int userId); void destroyUserKey(int userId);
void addUserKeyAuth(int userId, int userSerial, @utf8InCpp String secret); void setUserKeyProtection(int userId, @utf8InCpp String secret);
void clearUserKeyAuth(int userId, int userSerial, @utf8InCpp String secret);
void fixateNewestUserKeyAuth(int userId);
int[] getUnlockedUsers(); int[] getUnlockedUsers();
void unlockUserKey(int userId, int userSerial, @utf8InCpp String secret); void unlockUserKey(int userId, int userSerial, @utf8InCpp String secret);