From ca9a97ed6cd9244e866b6ab0c54129c32807b1ee Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 12 May 2022 19:17:15 +0000 Subject: [PATCH] Add and use prepare_dir_with_policy() helper function Having prepare_dir() and EnsurePolicy() be separate operations is error-prone; it lengthens the window of time that files could accidentally be created in new directories before they are encrypted, and it makes it easier to accidentally never encrypt a directory. To partially address this, add a function prepare_dir_with_policy() that combines the two steps, and use it everywhere possible. This function is now the only place in vold that calls EnsurePolicy(). As a follow-up change, we could go a bit further and make this helper function create the directory under a temporary name and move it into place already-encrypted. This change just focuses on getting the helper function in place, without changing the behavior too much. Change-Id: I98ab345df235120db6727f7dbe0da6a8b6ef2579 --- FsCrypt.cpp | 156 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 64 deletions(-) diff --git a/FsCrypt.cpp b/FsCrypt.cpp index 7a5178d..e253aa9 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -314,6 +314,9 @@ static bool read_and_install_user_ce_key(userid_t user_id, return true; } +// Prepare a directory without assigning it an encryption policy. The directory +// will inherit the encryption policy of its parent directory, or will be +// unencrypted if the parent directory is unencrypted. static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) { LOG(DEBUG) << "Preparing: " << dir; if (android::vold::PrepareDir(dir, mode, uid, gid, 0) != 0) { @@ -323,6 +326,14 @@ static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gi return true; } +// Prepare a directory and assign it the given encryption policy. +static bool prepare_dir_with_policy(const std::string& dir, mode_t mode, uid_t uid, gid_t gid, + const EncryptionPolicy& policy) { + if (!prepare_dir(dir, mode, uid, gid)) return false; + if (fscrypt_is_native() && !EnsurePolicy(policy, dir)) return false; + return true; +} + static bool destroy_dir(const std::string& dir) { LOG(DEBUG) << "Destroying: " << dir; if (rmdir(dir.c_str()) != 0 && errno != ENOENT) { @@ -372,7 +383,6 @@ static bool lookup_policy(const std::map& key_map, u EncryptionPolicy* policy) { auto refi = key_map.find(user_id); if (refi == key_map.end()) { - LOG(DEBUG) << "Cannot find key for " << user_id; return false; } *policy = refi->second; @@ -479,18 +489,29 @@ bool fscrypt_initialize_systemwide_keys() { } static bool prepare_special_dirs() { - // Create /data/user/0 and its bind-mount alias /data/data. This *should* - // happen in fscrypt_prepare_user_storage(). However, it actually must be - // done early, before the rest of user 0's CE storage is prepared. This is - // because zygote may need to set up app data isolation before then, which - // requires mounting a tmpfs over /data/data to ensure it remains hidden. - // This issue arises due to /data/data being in the top-level directory. - // + // Ensure that /data/data and its "alias" /data/user/0 exist, and create the + // bind mount of /data/data onto /data/user/0. This *should* happen in + // fscrypt_prepare_user_storage(). However, it actually must be done early, + // before the rest of user 0's CE storage is prepared. This is because + // zygote may need to set up app data isolation before then, which requires + // mounting a tmpfs over /data/data to ensure it remains hidden. This issue + // arises due to /data/data being in the top-level directory. + // /data/user/0 used to be a symlink to /data/data, so we must first delete // the old symlink if present. if (android::vold::IsSymlink(data_user_0_dir) && android::vold::Unlink(data_user_0_dir) != 0) return false; - if (!prepare_dir(data_data_dir, 0771, AID_SYSTEM, AID_SYSTEM)) return false; + // On first boot, we'll be creating /data/data for the first time, and user + // 0's CE key will be installed already since it was just created. Take the + // opportunity to also set the encryption policy of /data/data right away. + EncryptionPolicy 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)) + return false; + } else { + if (!prepare_dir(data_data_dir, 0771, AID_SYSTEM, AID_SYSTEM)) return false; + // EnsurePolicy() will have to happen later, in fscrypt_prepare_user_storage(). + } if (!prepare_dir(data_user_0_dir, 0700, AID_SYSTEM, AID_SYSTEM)) return false; if (android::vold::BindMount(data_data_dir, data_user_0_dir) != 0) return false; @@ -503,9 +524,13 @@ static bool prepare_special_dirs() { // // We must tolerate /data/media/obb being unencrypted if it already exists // on-disk, since it used to be unencrypted (b/64566063). - bool encrypt_obb = !android::vold::pathExists(media_obb_dir) && fscrypt_is_native(); - if (!prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false; - if (encrypt_obb && !EnsurePolicy(s_device_policy, media_obb_dir)) return false; + if (android::vold::pathExists(media_obb_dir)) { + if (!prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false; + } else { + if (!prepare_dir_with_policy(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW, + s_device_policy)) + return false; + } return true; } @@ -514,25 +539,23 @@ bool fscrypt_init_user0_done; bool fscrypt_init_user0() { LOG(DEBUG) << "fscrypt_init_user0"; - if (!prepare_special_dirs()) return false; - if (fscrypt_is_native()) { 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 (!android::vold::pathExists(get_de_key_path(0))) { if (!create_and_install_user_keys(0, false)) return false; - // Since /data/user/0 was created already, and directory creation - // and encryption should always happen together, encrypt - // /data/user/0 right away without waiting for the request to - // prepare user 0's CE storage. - if (!EnsurePolicy(s_ce_policies[0], data_user_0_dir)) return false; } // TODO: switch to loading only DE_0 here once framework makes // explicit calls to install DE keys for secondary users if (!load_all_de_keys()) return false; } - // We only prepare DE storage here, since user 0's CE key won't be installed + + // Now that user 0's CE key has been created, we can prepare /data/data. + if (!prepare_special_dirs()) return false; + + // With the exception of what is done by prepare_special_dirs() above, we + // only prepare DE storage here, since user 0's CE key won't be installed // yet unless it was just created. The framework will prepare the user's CE // storage later, once their CE key is installed. if (!fscrypt_prepare_user_storage("", 0, 0, android::os::IVold::STORAGE_FLAG_DE)) { @@ -883,11 +906,26 @@ bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_ auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id); // DE_n key + EncryptionPolicy de_policy; auto system_de_path = android::vold::BuildDataSystemDePath(user_id); auto misc_de_path = android::vold::BuildDataMiscDePath(volume_uuid, user_id); auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id); auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id); + if (fscrypt_is_native()) { + if (volume_uuid.empty()) { + if (!lookup_policy(s_de_policies, user_id, &de_policy)) { + LOG(ERROR) << "Cannot find DE policy for user " << user_id; + return false; + } + } else { + auto misc_de_empty_volume_path = android::vold::BuildDataMiscDePath("", user_id); + if (!read_or_create_volkey(misc_de_empty_volume_path, volume_uuid, &de_policy)) { + return false; + } + } + } + if (volume_uuid.empty()) { if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false; #if MANAGE_MISC_DIRS @@ -897,43 +935,49 @@ bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_ #endif if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; - if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false; - if (!prepare_dir(vendor_de_path, 0771, AID_ROOT, AID_ROOT)) return false; + if (!prepare_dir_with_policy(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM, de_policy)) + return false; + if (!prepare_dir_with_policy(vendor_de_path, 0771, AID_ROOT, AID_ROOT, de_policy)) + return false; } - if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false; - if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; - - if (fscrypt_is_native()) { - EncryptionPolicy de_policy; - if (volume_uuid.empty()) { - if (!lookup_policy(s_de_policies, user_id, &de_policy)) return false; - if (!EnsurePolicy(de_policy, system_de_path)) return false; - if (!EnsurePolicy(de_policy, vendor_de_path)) return false; - } else { - auto misc_de_empty_volume_path = android::vold::BuildDataMiscDePath("", user_id); - if (!read_or_create_volkey(misc_de_empty_volume_path, volume_uuid, &de_policy)) { - return false; - } - } - if (!EnsurePolicy(de_policy, misc_de_path)) return false; - if (!EnsurePolicy(de_policy, user_de_path)) return false; - } + if (!prepare_dir_with_policy(misc_de_path, 01771, AID_SYSTEM, AID_MISC, de_policy)) + return false; + if (!prepare_dir_with_policy(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM, de_policy)) + return false; } if (flags & android::os::IVold::STORAGE_FLAG_CE) { // CE_n key + EncryptionPolicy ce_policy; auto system_ce_path = android::vold::BuildDataSystemCePath(user_id); auto misc_ce_path = android::vold::BuildDataMiscCePath(volume_uuid, user_id); auto vendor_ce_path = android::vold::BuildDataVendorCePath(user_id); auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id); auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id); - if (volume_uuid.empty()) { - if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false; - if (!prepare_dir(vendor_ce_path, 0771, AID_ROOT, AID_ROOT)) return false; + if (fscrypt_is_native()) { + if (volume_uuid.empty()) { + if (!lookup_policy(s_ce_policies, user_id, &ce_policy)) { + LOG(ERROR) << "Cannot find CE policy for user " << user_id; + return false; + } + } else { + auto misc_ce_empty_volume_path = android::vold::BuildDataMiscCePath("", user_id); + if (!read_or_create_volkey(misc_ce_empty_volume_path, volume_uuid, &ce_policy)) { + return false; + } + } } - if (!prepare_dir(media_ce_path, 02770, AID_MEDIA_RW, AID_MEDIA_RW)) return false; + + if (volume_uuid.empty()) { + if (!prepare_dir_with_policy(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM, ce_policy)) + return false; + if (!prepare_dir_with_policy(vendor_ce_path, 0771, AID_ROOT, AID_ROOT, ce_policy)) + return false; + } + if (!prepare_dir_with_policy(media_ce_path, 02770, AID_MEDIA_RW, AID_MEDIA_RW, ce_policy)) + return false; // On devices without sdcardfs (kernel 5.4+), the path permissions aren't fixed // up automatically; therefore, use a default ACL, to ensure apps with MEDIA_RW // can keep reading external storage; in particular, this allows app cloning @@ -942,26 +986,10 @@ bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_ if (ret != android::OK) { return false; } - - if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false; - if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; - - if (fscrypt_is_native()) { - EncryptionPolicy ce_policy; - if (volume_uuid.empty()) { - if (!lookup_policy(s_ce_policies, user_id, &ce_policy)) return false; - if (!EnsurePolicy(ce_policy, system_ce_path)) return false; - if (!EnsurePolicy(ce_policy, vendor_ce_path)) return false; - } else { - auto misc_ce_empty_volume_path = android::vold::BuildDataMiscCePath("", user_id); - if (!read_or_create_volkey(misc_ce_empty_volume_path, volume_uuid, &ce_policy)) { - return false; - } - } - if (!EnsurePolicy(ce_policy, media_ce_path)) return false; - if (!EnsurePolicy(ce_policy, misc_ce_path)) return false; - if (!EnsurePolicy(ce_policy, user_ce_path)) return false; - } + if (!prepare_dir_with_policy(misc_ce_path, 01771, AID_SYSTEM, AID_MISC, ce_policy)) + return false; + if (!prepare_dir_with_policy(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM, ce_policy)) + return false; if (volume_uuid.empty()) { // Now that credentials have been installed, we can run restorecon