From 8c1659e27155a5c52f5739f1a0c82576a672ce28 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 6 Sep 2022 21:29:14 +0000 Subject: [PATCH 01/11] Make the CE key always be encrypted by the synthetic password When generating a CE key, don't persist it immediately with kEmptyAuthentication. Instead, cache it in memory and persist it later when the secret to protect it with is given. This is needed to make it so that the CE key is always encrypted by the user's synthetic password while it is stored on-disk. See the corresponding system_server changes for more information about this design change and its motivation. As part of this, simplify vold's Binder interface by replacing the three methods addUserKeyAuth(), clearUserKeyAuth(), and fixateNewestUserKeyAuth() with a single method setUserKeyProtection(). setUserKeyProtection() handles persisting the key for a new user or re-encrypting the default-encrypted key for an existing unsecured user. Bug: 232452368 Ignore-AOSP-First: This depends on frameworks/base changes that can only be submitted to internal master, due to conflicts. Test: see Ia753ea21bbaca8ef7a90c03fe73b66c896b1536e Change-Id: Id36ba8ee343ccb6de7ec892c3f600abd636f6ce5 --- FsCrypt.cpp | 125 +++++++++++++++++------------------ FsCrypt.h | 4 +- KeyStorage.cpp | 2 +- VoldNativeService.cpp | 20 +----- VoldNativeService.h | 4 +- binder/android/os/IVold.aidl | 4 +- 6 files changed, 68 insertions(+), 91 deletions(-) diff --git a/FsCrypt.cpp b/FsCrypt.cpp index 5bc55d0..68339f6 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -99,6 +99,9 @@ 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 std::set s_ephemeral_users; +// New CE keys that haven't been committed to disk yet +std::map s_new_ce_keys; + // The system DE encryption policy EncryptionPolicy s_device_policy; @@ -176,8 +179,7 @@ static bool get_ce_key_new_path(const std::string& directory_path, } // Discard all keys but the named one; rename it to canonical name. -// No point in acting on errors in this; ignore them. -static void fixate_user_ce_key(const std::string& directory_path, const std::string& to_fix, +static bool fixate_user_ce_key(const std::string& directory_path, const std::string& to_fix, const std::vector& paths) { for (auto const other_path : paths) { if (other_path != to_fix) { @@ -187,9 +189,10 @@ static void fixate_user_ce_key(const std::string& directory_path, const std::str auto const current_path = get_ce_key_current_path(directory_path); if (to_fix != 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; } - android::vold::FsyncDirectory(directory_path); + if (!android::vold::FsyncDirectory(directory_path)) return false; + return true; } static bool read_and_fixate_user_ce_key(userid_t user_id, @@ -351,14 +354,10 @@ static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral } else { auto const directory_path = get_ce_key_directory_path(user_id); if (!prepare_dir(directory_path, 0700, AID_ROOT, AID_ROOT)) return false; - 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 (!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. + // Wait until fscrypt_set_user_key_protection() to persist the CE key, + // since here we don't have the secret needed to do so securely. + s_new_ce_keys.insert({user_id, ce_key}); + if (!android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp, kEmptyAuthentication, de_key)) return false; @@ -616,6 +615,7 @@ static bool evict_ce_key(userid_t user_id) { drop_caches_if_needed(); } s_ce_policies.erase(user_id); + s_new_ce_keys.erase(user_id); return success; } @@ -630,13 +630,12 @@ bool fscrypt_destroy_user_key(userid_t user_id) { success &= lookup_policy(s_de_policies, user_id, &de_policy) && android::vold::evictKey(DATA_MNT_POINT, de_policy); s_de_policies.erase(user_id); - auto it = s_ephemeral_users.find(user_id); - if (it != s_ephemeral_users.end()) { - s_ephemeral_users.erase(it); - } else { + if (!s_ephemeral_users.erase(user_id)) { auto ce_path = get_ce_key_directory_path(user_id); - for (auto const path : get_ce_key_paths(ce_path)) { - success &= android::vold::destroyKey(path); + if (!s_new_ce_keys.erase(user_id)) { + for (auto const path : get_ce_key_paths(ce_path)) { + success &= android::vold::destroyKey(path); + } } success &= destroy_dir(ce_path); @@ -712,59 +711,59 @@ static bool destroy_volkey(const std::string& misc_path, const std::string& volu return android::vold::destroyKey(path); } -static bool fscrypt_rewrap_user_key(userid_t user_id, int serial, - const android::vold::KeyAuthentication& retrieve_auth, - const android::vold::KeyAuthentication& store_auth) { - if (s_ephemeral_users.count(user_id) != 0) return true; +// (Re-)encrypts the user's CE key with the given secret. The CE key must +// either be (a) new (not yet committed), (b) protected by kEmptyAuthentication, +// or (c) already protected by the given secret. Cases (b) and (c) are needed +// to support upgrades from Android versions where CE keys were stored with +// kEmptyAuthentication when the user didn't have an LSKF. Case (b) is the +// normal upgrade case, while case (c) can theoretically happen if an upgrade is +// requested for a user more than once due to a power-off or other interruption. +bool fscrypt_set_user_key_protection(userid_t user_id, const std::string& secret_hex) { + LOG(DEBUG) << "fscrypt_set_user_key_protection " << user_id; + if (!IsFbeEnabled()) return true; + auto auth = authentication_from_hex(secret_hex); + if (!auth) return false; + if (auth->secret.empty()) { + LOG(ERROR) << "fscrypt_set_user_key_protection: secret must be nonempty"; + return false; + } + if (s_ephemeral_users.count(user_id) != 0) { + LOG(DEBUG) << "Not storing key because user is ephemeral"; + return true; + } auto const directory_path = get_ce_key_directory_path(user_id); KeyBuffer ce_key; - std::string ce_key_current_path = get_ce_key_current_path(directory_path); - if (retrieveKey(ce_key_current_path, retrieve_auth, &ce_key)) { - LOG(DEBUG) << "Successfully retrieved key"; - // TODO(147732812): Remove this once Locksettingservice is fixed. - // Currently it calls fscrypt_clear_user_key_auth with a secret when lockscreen is - // changed from swipe to none or vice-versa - } else if (retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key)) { - LOG(DEBUG) << "Successfully retrieved key with empty auth"; + auto it = s_new_ce_keys.find(user_id); + if (it != s_new_ce_keys.end()) { + // Committing the key for a new user. This happens when the user's + // synthetic password is created. + ce_key = it->second; } else { - LOG(ERROR) << "Failed to retrieve key for user " << user_id; - return false; + // 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) << "Key already exists; re-protecting it with the given secret"; + if (!read_and_fixate_user_ce_key(user_id, kEmptyAuthentication, &ce_key)) { + LOG(ERROR) << "Failed to retrieve 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) << "Key is already protected by given secret"; + return true; + } + return false; + } } 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 (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, store_auth, ce_key)) - return false; - return true; -} - -bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& secret_hex) { - LOG(DEBUG) << "fscrypt_add_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, kEmptyAuthentication, *auth); -} - -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; + if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, *auth, ce_key)) return false; + 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; } - fixate_user_ce_key(directory_path, paths[0], paths); return true; } diff --git a/FsCrypt.h b/FsCrypt.h index e5af487..2cb47eb 100644 --- a/FsCrypt.h +++ b/FsCrypt.h @@ -25,9 +25,7 @@ bool fscrypt_init_user0(); extern bool fscrypt_init_user0_done; 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_add_user_key_auth(userid_t user_id, int serial, const std::string& secret); -bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& secret); -bool fscrypt_fixate_newest_user_key_auth(userid_t user_id); +bool fscrypt_set_user_key_protection(userid_t user_id, const std::string& secret); std::vector fscrypt_get_unlocked_users(); bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& secret); diff --git a/KeyStorage.cpp b/KeyStorage.cpp index b4abc27..56fa9b4 100644 --- a/KeyStorage.cpp +++ b/KeyStorage.cpp @@ -616,7 +616,7 @@ bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path if (!RenameKeyDir(tmp_path, key_path)) return false; if (!FsyncParentDirectory(key_path)) return false; - LOG(DEBUG) << "Created key: " << key_path; + LOG(DEBUG) << "Stored key " << key_path; return true; } diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp index ea2c98c..be0fa28 100644 --- a/VoldNativeService.cpp +++ b/VoldNativeService.cpp @@ -610,27 +610,11 @@ binder::Status VoldNativeService::destroyUserKey(int32_t userId) { return translateBool(fscrypt_destroy_user_key(userId)); } -binder::Status VoldNativeService::addUserKeyAuth(int32_t userId, int32_t userSerial, - const std::string& secret) { +binder::Status VoldNativeService::setUserKeyProtection(int32_t userId, const std::string& secret) { ENFORCE_SYSTEM_OR_ROOT; ACQUIRE_CRYPT_LOCK; - return translateBool(fscrypt_add_user_key_auth(userId, userSerial, 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)); + return translateBool(fscrypt_set_user_key_protection(userId, secret)); } binder::Status VoldNativeService::getUnlockedUsers(std::vector* _aidl_return) { diff --git a/VoldNativeService.h b/VoldNativeService.h index 37a988b..560118b 100644 --- a/VoldNativeService.h +++ b/VoldNativeService.h @@ -115,9 +115,7 @@ class VoldNativeService : public BinderService, public os::Bn binder::Status createUserKey(int32_t userId, int32_t userSerial, bool ephemeral); binder::Status destroyUserKey(int32_t userId); - binder::Status addUserKeyAuth(int32_t userId, int32_t userSerial, 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 setUserKeyProtection(int32_t userId, const std::string& secret); binder::Status getUnlockedUsers(std::vector* _aidl_return); binder::Status unlockUserKey(int32_t userId, int32_t userSerial, const std::string& secret); diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl index 77478d9..3a09969 100644 --- a/binder/android/os/IVold.aidl +++ b/binder/android/os/IVold.aidl @@ -88,9 +88,7 @@ interface IVold { void createUserKey(int userId, int userSerial, boolean ephemeral); void destroyUserKey(int userId); - void addUserKeyAuth(int userId, int userSerial, @utf8InCpp String secret); - void clearUserKeyAuth(int userId, int userSerial, @utf8InCpp String secret); - void fixateNewestUserKeyAuth(int userId); + void setUserKeyProtection(int userId, @utf8InCpp String secret); int[] getUnlockedUsers(); void unlockUserKey(int userId, int userSerial, @utf8InCpp String secret); From 9544f8c7b2ca58a7f72da10b7a4beefd6da5e360 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 7 Oct 2022 19:07:23 +0000 Subject: [PATCH 02/11] Regenerate user 0's CE key when needed Try to be more robust in the case where the device is rebooted during the first boot, in between the generation and the storage of user 0's CE key. We can automatically recover from this scenario by generating a new CE key and replacing /data/data. This might resolve b/251213447. Bug: 232452368 Bug: 251213447 Ignore-AOSP-First: depends on other changes in internal master Change-Id: If0675de9167f7f855c0c0c6afe55fd1da39f5ce1 --- FsCrypt.cpp | 102 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/FsCrypt.cpp b/FsCrypt.cpp index 68339f6..befffdf 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -340,35 +340,74 @@ static bool destroy_dir(const std::string& dir) { return true; } -// NB this assumes that there is only one thread listening for crypt commands, because -// it creates keys in a fixed location. +// Checks whether at least one CE key subdirectory exists. +static bool ce_key_exists(const std::string& directory_path) { + // The common case is that "$dir/current" exists, so check for that first. + if (android::vold::pathExists(get_ce_key_current_path(directory_path))) return true; + + // Else, there could still be another subdirectory of $dir (if a crash + // occurred during fixate_user_ce_key()), so check for one. + return android::vold::pathExists(directory_path) && !get_ce_key_paths(directory_path).empty(); +} + +// Creates and installs the CE and DE keys for the given user, as needed. +// +// We store the DE key right away. We don't store the CE key yet, because the +// secret needed to do so securely isn't available yet. Instead, we cache the +// CE key in memory and store it later in fscrypt_set_user_key_protection(). +// +// For user 0, this function is called on every boot, so we need to create the +// keys only if they weren't already stored. In doing so, we must consider the +// DE and CE keys independently, since the first boot might have been +// interrupted between the DE key being stored and the CE key being stored. +// +// For other users, this is only called at user creation time, and neither key +// directory should exist already. |create_ephemeral| means that the user is +// ephemeral; in that case the keys are generated and installed, but not stored. static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) { EncryptionOptions options; if (!get_data_file_encryption_options(&options)) return false; - KeyBuffer de_key, ce_key; - if (!generateStorageKey(makeGen(options), &de_key)) return false; - if (!generateStorageKey(makeGen(options), &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); - } else { - auto const directory_path = get_ce_key_directory_path(user_id); - if (!prepare_dir(directory_path, 0700, AID_ROOT, AID_ROOT)) return false; - // Wait until fscrypt_set_user_key_protection() to persist the CE key, - // since here we don't have the secret needed to do so securely. - s_new_ce_keys.insert({user_id, ce_key}); - if (!android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp, - kEmptyAuthentication, de_key)) + auto de_key_path = get_de_key_path(user_id); + if (create_ephemeral || !android::vold::pathExists(de_key_path)) { + KeyBuffer de_key; + if (!generateStorageKey(makeGen(options), &de_key)) return false; + if (!create_ephemeral && !android::vold::storeKeyAtomically(de_key_path, user_key_temp, + kEmptyAuthentication, de_key)) return false; + EncryptionPolicy de_policy; + if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false; + s_de_policies[user_id] = de_policy; + LOG(INFO) << "Created DE key for user " << user_id; + } else { + if (user_id != 0) { + LOG(ERROR) << "DE key already exists on disk"; + return false; + } + } + + auto ce_path = get_ce_key_directory_path(user_id); + if (create_ephemeral || !ce_key_exists(ce_path)) { + KeyBuffer ce_key; + if (!generateStorageKey(makeGen(options), &ce_key)) return false; + if (!create_ephemeral) { + if (!prepare_dir(ce_path, 0700, AID_ROOT, AID_ROOT)) return false; + s_new_ce_keys.insert({user_id, ce_key}); + } + EncryptionPolicy ce_policy; + if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false; + s_ce_policies[user_id] = ce_policy; + LOG(INFO) << "Created CE key for user " << user_id; + } else { + if (user_id != 0) { + LOG(ERROR) << "CE key already exists on disk"; + return false; + } + } + + if (create_ephemeral) { + s_ephemeral_users.insert(user_id); } - EncryptionPolicy de_policy; - if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false; - s_de_policies[user_id] = de_policy; - EncryptionPolicy ce_policy; - if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false; - s_ce_policies[user_id] = ce_policy; - LOG(DEBUG) << "Created keys for user " << user_id; return true; } @@ -499,8 +538,17 @@ static bool prepare_special_dirs() { // 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; + 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)) + 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(). @@ -536,9 +584,7 @@ bool fscrypt_init_user0() { 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; - } + if (!create_and_install_user_keys(0, false)) 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; From ce50a4332279af0a5ae009bd3c5efb72f8467f2d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 19 Oct 2022 19:38:50 +0000 Subject: [PATCH 03/11] Regenerate CE key for non-system users when needed Try to be more robust in the case where the device is rebooted during the first boot, in between the generation and the storage of the CE key for a user other than user 0. This is relevant when users are created during early boot, which Automotive devices do. Bug: 232452368 Bug: 251213447 Ignore-AOSP-First: depends on other changes in internal master Change-Id: Ic8f19a36c1385a71a168a330e87675433925a60f --- FsCrypt.cpp | 154 +++++++++++++++++++++++++++------------------------- 1 file changed, 79 insertions(+), 75 deletions(-) diff --git a/FsCrypt.cpp b/FsCrypt.cpp index befffdf..a3ef9ba 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -96,7 +96,7 @@ 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 media_obb_dir = std::string() + DATA_MNT_POINT + "/media/obb"; -// Some users are ephemeral, don't try to wipe their keys from disk +// Some users are ephemeral; don't try to store or wipe their keys on disk. std::set s_ephemeral_users; // New CE keys that haven't been committed to disk yet @@ -340,8 +340,14 @@ static bool destroy_dir(const std::string& dir) { return true; } -// Checks whether at least one CE key subdirectory exists. -static bool ce_key_exists(const std::string& directory_path) { +// Checks whether the DE key directory exists for the given user. +static bool de_key_exists(userid_t user_id) { + return android::vold::pathExists(get_de_key_path(user_id)); +} + +// Checks whether at least one CE key subdirectory exists for the given user. +static bool ce_key_exists(userid_t user_id) { + auto directory_path = get_ce_key_directory_path(user_id); // The common case is that "$dir/current" exists, so check for that first. if (android::vold::pathExists(get_ce_key_current_path(directory_path))) return true; @@ -350,64 +356,40 @@ static bool ce_key_exists(const std::string& directory_path) { return android::vold::pathExists(directory_path) && !get_ce_key_paths(directory_path).empty(); } -// Creates and installs the CE and DE keys for the given user, as needed. -// -// We store the DE key right away. We don't store the CE key yet, because the -// secret needed to do so securely isn't available yet. Instead, we cache the -// CE key in memory and store it later in fscrypt_set_user_key_protection(). -// -// For user 0, this function is called on every boot, so we need to create the -// keys only if they weren't already stored. In doing so, we must consider the -// DE and CE keys independently, since the first boot might have been -// interrupted between the DE key being stored and the CE key being stored. -// -// For other users, this is only called at user creation time, and neither key -// directory should exist already. |create_ephemeral| means that the user is -// ephemeral; in that case the keys are generated and installed, but not stored. -static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) { +static bool create_de_key(userid_t user_id, bool ephemeral) { EncryptionOptions options; if (!get_data_file_encryption_options(&options)) return false; - auto de_key_path = get_de_key_path(user_id); - if (create_ephemeral || !android::vold::pathExists(de_key_path)) { - KeyBuffer de_key; - if (!generateStorageKey(makeGen(options), &de_key)) return false; - if (!create_ephemeral && !android::vold::storeKeyAtomically(de_key_path, user_key_temp, - kEmptyAuthentication, de_key)) - return false; - EncryptionPolicy de_policy; - if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false; - s_de_policies[user_id] = de_policy; - LOG(INFO) << "Created DE key for user " << user_id; - } else { - if (user_id != 0) { - LOG(ERROR) << "DE key already exists on disk"; - return false; - } - } + KeyBuffer de_key; + if (!generateStorageKey(makeGen(options), &de_key)) return false; + if (!ephemeral && !android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp, + kEmptyAuthentication, de_key)) + return false; + EncryptionPolicy de_policy; + if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false; + s_de_policies[user_id] = de_policy; + LOG(INFO) << "Created DE key for user " << user_id; + return true; +} - auto ce_path = get_ce_key_directory_path(user_id); - if (create_ephemeral || !ce_key_exists(ce_path)) { - KeyBuffer ce_key; - if (!generateStorageKey(makeGen(options), &ce_key)) return false; - if (!create_ephemeral) { - if (!prepare_dir(ce_path, 0700, AID_ROOT, AID_ROOT)) return false; - s_new_ce_keys.insert({user_id, ce_key}); - } - EncryptionPolicy ce_policy; - if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false; - s_ce_policies[user_id] = ce_policy; - LOG(INFO) << "Created CE key for user " << user_id; - } else { - if (user_id != 0) { - LOG(ERROR) << "CE key already exists on disk"; - return false; - } - } +static bool create_ce_key(userid_t user_id, bool ephemeral) { + EncryptionOptions options; + if (!get_data_file_encryption_options(&options)) return false; - if (create_ephemeral) { - s_ephemeral_users.insert(user_id); + KeyBuffer ce_key; + if (!generateStorageKey(makeGen(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; + if (!install_storage_key(DATA_MNT_POINT, options, ce_key, &ce_policy)) return false; + s_ce_policies[user_id] = ce_policy; + LOG(INFO) << "Created CE key for user " << user_id; return true; } @@ -584,7 +566,13 @@ bool fscrypt_init_user0() { 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 (!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 // explicit calls to install DE keys for secondary users if (!load_all_de_keys()) return false; @@ -625,9 +613,9 @@ bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) // FIXME should we fail the command? return true; } - if (!create_and_install_user_keys(user_id, ephemeral)) { - return false; - } + if (!create_de_key(user_id, ephemeral)) return false; + if (!create_ce_key(user_id, ephemeral)) return false; + if (ephemeral) s_ephemeral_users.insert(user_id); return true; } @@ -757,13 +745,11 @@ static bool destroy_volkey(const std::string& misc_path, const std::string& volu return android::vold::destroyKey(path); } -// (Re-)encrypts the user's CE key with the given secret. The CE key must -// either be (a) new (not yet committed), (b) protected by kEmptyAuthentication, -// or (c) already protected by the given secret. Cases (b) and (c) are needed -// to support upgrades from Android versions where CE keys were stored with -// kEmptyAuthentication when the user didn't have an LSKF. Case (b) is the -// normal upgrade case, while case (c) can theoretically happen if an upgrade is -// requested for a user more than once due to a power-off or other interruption. +// (Re-)encrypts the user's CE key with the given secret. This function handles +// storing the CE key for a new user for the first time. It also handles +// re-encrypting the CE key upon upgrade from an Android version where the CE +// key was stored with kEmptyAuthentication when the user didn't have an LSKF. +// See the comments below for the different cases handled. bool fscrypt_set_user_key_protection(userid_t user_id, const std::string& secret_hex) { LOG(DEBUG) << "fscrypt_set_user_key_protection " << user_id; if (!IsFbeEnabled()) return true; @@ -773,35 +759,53 @@ bool fscrypt_set_user_key_protection(userid_t user_id, const std::string& secret LOG(ERROR) << "fscrypt_set_user_key_protection: secret must be nonempty"; 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; } - auto const directory_path = get_ce_key_directory_path(user_id); KeyBuffer ce_key; auto it = s_new_ce_keys.find(user_id); if (it != s_new_ce_keys.end()) { - // Committing the key for a new user. This happens when the user's - // synthetic password is created. + // 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 { - // Setting the protection on an existing key. This happens at upgrade - // time, when CE keys that were previously protected by + } 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) << "Key already exists; re-protecting it with the given secret"; + 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 key for user " << user_id << " using empty auth"; + 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) << "Key is already protected by given secret"; + 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); std::string ce_key_path; if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false; From 11409cbf307b685693c320383ce27639591c41a2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 26 Oct 2022 19:02:43 +0000 Subject: [PATCH 04/11] Don't unconditionally sync directory in fixate_user_ce_key() Directory syncs can be expensive, so only sync the directory in fixate_user_ce_key() if something was actually done, i.e. if at least one key directory was deleted or renamed. Previously, the unconditional sync in this function was being executed whenever the CE key was retrieved or stored. Note that all the syncs needed when storing the key already happen in storeKeyAtomically(); this one was unrelated. Bug: 232452368 Bug: 251131631 Bug: 251147505 Ignore-AOSP-First: depends on other changes in internal master Change-Id: Ib0f2b9e27cdd11e359a1618cddc1f5480bd2fd37 --- FsCrypt.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/FsCrypt.cpp b/FsCrypt.cpp index a3ef9ba..d964a33 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -181,17 +181,20 @@ static bool get_ce_key_new_path(const std::string& directory_path, // Discard all keys but the named one; rename it to canonical name. static bool fixate_user_ce_key(const std::string& directory_path, const std::string& to_fix, const std::vector& paths) { + bool need_sync = false; for (auto const other_path : paths) { if (other_path != to_fix) { android::vold::destroyKey(other_path); + need_sync = true; } } auto const current_path = get_ce_key_current_path(directory_path); if (to_fix != current_path) { LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path; if (!android::vold::RenameKeyDir(to_fix, current_path)) return false; + need_sync = true; } - if (!android::vold::FsyncDirectory(directory_path)) return false; + if (need_sync && !android::vold::FsyncDirectory(directory_path)) return false; return true; } From 4cf16915f34bf7cd0c56d27a1fa1919bf5cccfa1 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 26 Oct 2022 19:05:10 +0000 Subject: [PATCH 05/11] Initialize the /data encryption options only once Cache the EncryptionOptions for /data in a static variable so that it doesn't have to be repeatedly regenerated from the fstab. Bug: 232452368 Bug: 251131631 Bug: 251147505 Ignore-AOSP-First: depends on other changes in internal master Change-Id: I24b27190ed807f142b793d3cf250ec271d092f34 --- FsCrypt.cpp | 54 +++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/FsCrypt.cpp b/FsCrypt.cpp index d964a33..477db7c 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -96,6 +96,9 @@ 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 media_obb_dir = std::string() + DATA_MNT_POINT + "/media/obb"; +// 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 s_ephemeral_users; @@ -113,6 +116,10 @@ std::map s_ce_policies; // Returns KeyGeneration suitable for key as described in EncryptionOptions 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}; } @@ -242,19 +249,19 @@ static bool MightBeEmmcStorage(const std::string& blk_device) { StartsWith(name, "vd"); } -// Retrieve the options to use for encryption policies on the /data filesystem. -static bool get_data_file_encryption_options(EncryptionOptions* options) { +// Sets s_data_options to the file encryption options for the /data filesystem. +static bool init_data_file_encryption_options() { auto entry = GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT); if (entry == nullptr) { LOG(ERROR) << "No mount point entry for " << DATA_MNT_POINT; 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 ": " << entry->encryption_options; 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)) { LOG(ERROR) << "The emmc_optimized encryption flag is only allowed on eMMC storage. Remove " "this flag from the device's fstab"; @@ -265,6 +272,10 @@ static bool get_data_file_encryption_options(EncryptionOptions* options) { static bool install_storage_key(const std::string& mountpoint, const EncryptionOptions& options, const KeyBuffer& key, EncryptionPolicy* policy) { + if (options.version == 0) { + LOG(ERROR) << "EncryptionOptions not initialized"; + return false; + } KeyBuffer ephemeral_wrapped_key; if (options.use_hw_wrapped_key) { if (!exportWrappedStorageKey(key, &ephemeral_wrapped_key)) { @@ -303,12 +314,10 @@ static bool get_volume_file_encryption_options(EncryptionOptions* options) { static bool read_and_install_user_ce_key(userid_t user_id, const android::vold::KeyAuthentication& auth) { if (s_ce_policies.count(user_id) != 0) return true; - EncryptionOptions options; - if (!get_data_file_encryption_options(&options)) return false; KeyBuffer ce_key; if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false; 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; LOG(DEBUG) << "Installed ce key for user " << user_id; return true; @@ -360,27 +369,21 @@ static bool ce_key_exists(userid_t user_id) { } static bool create_de_key(userid_t user_id, bool ephemeral) { - EncryptionOptions options; - if (!get_data_file_encryption_options(&options)) return false; - KeyBuffer de_key; - if (!generateStorageKey(makeGen(options), &de_key)) return false; + if (!generateStorageKey(makeGen(s_data_options), &de_key)) return false; if (!ephemeral && !android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp, kEmptyAuthentication, de_key)) return false; 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; LOG(INFO) << "Created DE key for user " << user_id; return true; } static bool create_ce_key(userid_t user_id, bool ephemeral) { - EncryptionOptions options; - if (!get_data_file_encryption_options(&options)) return false; - KeyBuffer ce_key; - if (!generateStorageKey(makeGen(options), &ce_key)) return false; + 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; @@ -390,7 +393,7 @@ static bool create_ce_key(userid_t user_id, bool ephemeral) { s_new_ce_keys.insert({user_id, ce_key}); } 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; LOG(INFO) << "Created CE key for user " << user_id; return true; @@ -414,8 +417,6 @@ static bool is_numeric(const char* name) { } 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 dirp = std::unique_ptr(opendir(de_dir.c_str()), closedir); if (!dirp) { @@ -442,7 +443,7 @@ static bool load_all_de_keys() { KeyBuffer de_key; if (!retrieveKey(key_path, kEmptyAuthentication, &de_key)) return false; 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}); if (!ret.second && ret.first->second != de_policy) { LOG(ERROR) << "DE policy for user" << user_id << " changed"; @@ -469,17 +470,17 @@ static bool try_reload_ce_keys() { bool fscrypt_initialize_systemwide_keys() { LOG(INFO) << "fscrypt_initialize_systemwide_keys"; - EncryptionOptions options; - if (!get_data_file_encryption_options(&options)) return false; + if (!init_data_file_encryption_options()) return false; KeyBuffer device_key; if (!retrieveOrGenerateKey(device_key_path, device_key_temp, kEmptyAuthentication, - makeGen(options), &device_key)) + makeGen(s_data_options), &device_key)) return false; // This initializes s_device_policy, which is a global variable so that // 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; if (!OptionsToString(s_device_policy.options, &options_string)) { @@ -494,9 +495,10 @@ bool fscrypt_initialize_systemwide_keys() { LOG(INFO) << "Wrote system DE key reference to:" << ref_filename; 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; - 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; if (!android::vold::writeStringToFile(per_boot_policy.key_raw_ref, per_boot_ref_filename)) return false; From 28858c9b3f531385634ab5a9dbea9bde806861de Mon Sep 17 00:00:00 2001 From: Alfred Piccioni Date: Wed, 21 Sep 2022 17:32:04 +0200 Subject: [PATCH 06/11] Add NTFS support in vold Ignore-AOSP-First: Internal CR while awaiting security and legal review. This CR, when paired with a functional NTFS implementation and the corresponding SEPolicy updates, will allow NTFS USB drives to be mounted on Android. Bug: 254407246 Test: Extensive testing with an ADT-4 and NTFS USB drives. Change-Id: If4197c4c588866c611cd6ba3483707d3cb0e0cf8 --- Android.bp | 1 + fs/Ntfs.cpp | 134 +++++++++++++++++++++++++++++++++++++++++ fs/Ntfs.h | 39 ++++++++++++ main.cpp | 3 +- model/PublicVolume.cpp | 54 ++++++++++++----- 5 files changed, 215 insertions(+), 16 deletions(-) create mode 100644 fs/Ntfs.cpp create mode 100644 fs/Ntfs.h diff --git a/Android.bp b/Android.bp index 1ccfc09..8f5cae3 100644 --- a/Android.bp +++ b/Android.bp @@ -138,6 +138,7 @@ cc_library_static { "fs/Ext4.cpp", "fs/F2fs.cpp", "fs/Vfat.cpp", + "fs/Ntfs.cpp", "model/Disk.cpp", "model/EmulatedVolume.cpp", "model/ObbVolume.cpp", diff --git a/fs/Ntfs.cpp b/fs/Ntfs.cpp new file mode 100644 index 0000000..6f57693 --- /dev/null +++ b/fs/Ntfs.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define LOG_TAG "Vold" + +#include +#include +#include +#include +#include + +#include + +#include "Ntfs.h" +#include "Utils.h" +#include "VoldUtil.h" + +using android::base::StringPrintf; + +namespace android { +namespace vold { +namespace ntfs { + +static const char* kFsckPath = "/system/bin/ntfsfix"; +static const char* kMkfsPath = "/system/bin/mkntfs"; + +static const char* fsName = "ntfs3"; + +bool IsSupported() { + return access(kFsckPath, X_OK) == 0 && access(kMkfsPath, X_OK) == 0 && + IsFilesystemSupported(fsName); +} + +status_t Check(const std::string& source) { + std::vector cmd; + cmd.push_back(kFsckPath); + + // ntfsfix sets the dirty bit by default, which prevents mounting the drive. + // -d tells it to instead reset the dirty bit. Technically, this could be dangerous, + // but since ntfsfix should report any errors with the drive and separately return + // a failed check, this should be relatively safe. + cmd.push_back("-d"); + + cmd.push_back(source); + + int rc = ForkExecvpTimeout(cmd, kUntrustedFsckSleepTime, sFsckUntrustedContext); + if (rc == 0) { + LOG(INFO) << "Check NTFS OK"; + return 0; + } else { + LOG(ERROR) << "Check NTFS failed (code " << rc << ")"; + errno = EIO; + return -1; + } +} + +status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount, + bool executable, int ownerUid, int ownerGid, int permMask) { + unsigned long flags = MS_NODEV | MS_NOSUID | MS_DIRSYNC | MS_NOATIME; + + flags |= (executable ? 0 : MS_NOEXEC); + flags |= (ro ? MS_RDONLY : 0); + flags |= (remount ? MS_REMOUNT : 0); + + // Android mount does not support "utf8" as an option. We use the deprecated iocharset instead. + auto mountData = android::base::StringPrintf("uid=%d,gid=%d,fmask=%o,dmask=%o,iocharset=%s", + ownerUid, ownerGid, permMask, permMask, "utf8"); + + int rc = mount(source.c_str(), target.c_str(), fsName, flags, mountData.c_str()); + + if (rc && errno == EROFS) { + LOG(ERROR) << source << " appears to be a read only filesystem - retrying mount RO"; + flags |= MS_RDONLY; + rc = mount(source.c_str(), target.c_str(), fsName, flags, mountData.c_str()); + } + + return rc; +} + +status_t Format(const std::string& source, unsigned int numSectors) { + std::vector cmd; + cmd.push_back(kMkfsPath); + cmd.push_back(source); + + if (numSectors) { + cmd.push_back(StringPrintf("%u", numSectors)); + } + + int rc = ForkExecvp(cmd); + if (rc == 0) { + LOG(INFO) << "Filesystem formatted OK"; + return 0; + } else { + LOG(ERROR) << "Format failed with error code: " << rc; + errno = EIO; + return -1; + } + return 0; +} + +} // namespace ntfs +} // namespace vold +} // namespace android \ No newline at end of file diff --git a/fs/Ntfs.h b/fs/Ntfs.h new file mode 100644 index 0000000..0049490 --- /dev/null +++ b/fs/Ntfs.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_VOLD_NTFS_H +#define ANDROID_VOLD_NTFS_H + +#include + +#include + +namespace android { +namespace vold { +namespace ntfs { + +bool IsSupported(); + +status_t Check(const std::string& source); +status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount, + bool executable, int ownerUid, int ownerGid, int permMask); +status_t Format(const std::string& source, unsigned int numSectors); + +} // namespace ntfs +} // namespace vold +} // namespace android + +#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index b07ee68..50a02ae 100644 --- a/main.cpp +++ b/main.cpp @@ -74,7 +74,8 @@ int main(int argc, char** argv) { LOG(DEBUG) << "Detected support for:" << (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "") << (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "") - << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : ""); + << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "") + << (android::vold::IsFilesystemSupported("ntfs3") ? " ntfs3" : ""); VolumeManager* vm; NetlinkManager* nm; diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp index bf54c95..20015af 100644 --- a/model/PublicVolume.cpp +++ b/model/PublicVolume.cpp @@ -20,6 +20,7 @@ #include "Utils.h" #include "VolumeManager.h" #include "fs/Exfat.h" +#include "fs/Ntfs.h" #include "fs/Vfat.h" #include @@ -110,6 +111,11 @@ status_t PublicVolume::doMount() { LOG(ERROR) << getId() << " failed filesystem check"; return -EIO; } + } else if (mFsType == "ntfs" && ntfs::IsSupported()) { + if (ntfs::Check(mDevPath)) { + LOG(ERROR) << getId() << " failed filesystem check"; + return -EIO; + } } else { LOG(ERROR) << getId() << " unsupported filesystem " << mFsType; return -EIO; @@ -152,6 +158,12 @@ status_t PublicVolume::doMount() { PLOG(ERROR) << getId() << " failed to mount " << mDevPath; return -EIO; } + } else if (mFsType == "ntfs") { + if (ntfs::Mount(mDevPath, mRawPath, false, false, false, AID_ROOT, + (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE), 0007)) { + PLOG(ERROR) << getId() << " failed to mount " << mDevPath; + return -EIO; + } } if (getMountFlags() & MountFlags::kPrimary) { @@ -308,12 +320,15 @@ status_t PublicVolume::doUnmount() { } status_t PublicVolume::doFormat(const std::string& fsType) { - bool useVfat = vfat::IsSupported(); - bool useExfat = exfat::IsSupported(); + bool isVfatSup = vfat::IsSupported(); + bool isExfatSup = exfat::IsSupported(); + bool isNtfsSup = ntfs::IsSupported(); status_t res = OK; - // Resolve the target filesystem type - if (fsType == "auto" && useVfat && useExfat) { + enum { NONE, VFAT, EXFAT, NTFS } fsPick = NONE; + + // Resolve auto requests + if (fsType == "auto" && isVfatSup && isExfatSup) { uint64_t size = 0; res = GetBlockDevSize(mDevPath, &size); @@ -324,29 +339,38 @@ status_t PublicVolume::doFormat(const std::string& fsType) { // If both vfat & exfat are supported use exfat for SDXC (>~32GiB) cards if (size > 32896LL * 1024 * 1024) { - useVfat = false; + fsPick = EXFAT; } else { - useExfat = false; + fsPick = VFAT; } - } else if (fsType == "vfat") { - useExfat = false; - } else if (fsType == "exfat") { - useVfat = false; + } else if (fsType == "auto" && isExfatSup) { + fsPick = EXFAT; + } else if (fsType == "auto" && isVfatSup) { + fsPick = VFAT; } - if (!useVfat && !useExfat) { - LOG(ERROR) << "Unsupported filesystem " << fsType; - return -EINVAL; + // Resolve explicit requests + if (fsType == "vfat" && isVfatSup) { + fsPick = VFAT; + } else if (fsType == "exfat" && isExfatSup) { + fsPick = EXFAT; + } else if (fsType == "ntfs" && isNtfsSup) { + fsPick = NTFS; } if (WipeBlockDevice(mDevPath) != OK) { LOG(WARNING) << getId() << " failed to wipe"; } - if (useVfat) { + if (fsPick == VFAT) { res = vfat::Format(mDevPath, 0); - } else if (useExfat) { + } else if (fsPick == EXFAT) { res = exfat::Format(mDevPath); + } else if (fsPick == NTFS) { + res = ntfs::Format(mDevPath, 0); + } else { + LOG(ERROR) << "Unsupported filesystem " << fsType; + return -EINVAL; } if (res != OK) { From b615f3beac9681f074f56234142931c66b6e8fbb Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 9 Nov 2022 05:48:45 +0000 Subject: [PATCH 07/11] Defer CE key fixations to checkpoint commit On the first boot after an upgrade, ensure that any Keystore key deletions triggered by fscrypt_set_user_key_protection() are deferred until the userdata filesystem checkpoint is committed, so that the system doesn't end up in a bad state if the checkpoint is rolled back. Test: see I77d30f9be57de7b7c4818680732331549ecb73c8 Bug: 232452368 Ignore-AOSP-First: depends on other changes in internal master Change-Id: I59b758bc13b7a2ae270f1a6c409affe2eb61119c --- Checkpoint.cpp | 15 +++++++++++++++ FsCrypt.cpp | 32 +++++++++++++++++++++++++++++++- FsCrypt.h | 1 + KeyStorage.cpp | 10 ++++------ KeyStorage.h | 2 ++ 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/Checkpoint.cpp b/Checkpoint.cpp index 948231d..3825af9 100644 --- a/Checkpoint.cpp +++ b/Checkpoint.cpp @@ -16,6 +16,8 @@ #define LOG_TAG "Checkpoint" #include "Checkpoint.h" +#include "FsCrypt.h" +#include "KeyStorage.h" #include "VoldUtil.h" #include "VolumeManager.h" @@ -78,6 +80,18 @@ bool setBowState(std::string const& block_device, std::string const& state) { 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 lock(VolumeManager::Instance()->getCryptLock()); + + DeferredCommitKeystoreKeys(); + fscrypt_deferred_fixate_ce_keys(); +} + } // namespace Status cp_supportsCheckpoint(bool& result) { @@ -205,6 +219,7 @@ Status cp_commitChanges() { if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str)) return error(err_str.c_str()); + std::thread(DoCheckpointCommittedWork).detach(); return Status::ok(); } diff --git a/FsCrypt.cpp b/FsCrypt.cpp index 477db7c..8df438f 100644 --- a/FsCrypt.cpp +++ b/FsCrypt.cpp @@ -16,6 +16,7 @@ #include "FsCrypt.h" +#include "Checkpoint.h" #include "KeyStorage.h" #include "KeyUtil.h" #include "Utils.h" @@ -112,6 +113,9 @@ EncryptionPolicy s_device_policy; std::map s_de_policies; std::map s_ce_policies; +// CE key fixation operations that have been deferred to checkpoint commit +std::map s_deferred_fixations; + } // namespace // Returns KeyGeneration suitable for key as described in EncryptionOptions @@ -214,6 +218,7 @@ static bool read_and_fixate_user_ce_key(userid_t user_id, LOG(DEBUG) << "Trying user CE key " << ce_key_path; if (retrieveKey(ce_key_path, auth, ce_key)) { LOG(DEBUG) << "Successfully retrieved key"; + s_deferred_fixations.erase(directory_path); fixate_user_ce_key(directory_path, ce_key_path, paths); return true; } @@ -676,6 +681,7 @@ bool fscrypt_destroy_user_key(userid_t user_id) { success &= android::vold::destroyKey(path); } } + s_deferred_fixations.erase(ce_path); success &= destroy_dir(ce_path); auto de_key_path = get_de_key_path(user_id); @@ -815,13 +821,37 @@ bool fscrypt_set_user_key_protection(userid_t user_id, const std::string& secret std::string ce_key_path; if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false; if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, *auth, ce_key)) return false; - if (!fixate_user_ce_key(directory_path, ce_key_path, paths)) 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; } +void fscrypt_deferred_fixate_ce_keys() { + for (const auto& it : s_deferred_fixations) { + const auto& directory_path = it.first; + const auto& to_fix = it.second; + LOG(INFO) << "Doing deferred fixation of " << directory_path; + fixate_user_ce_key(directory_path, to_fix, get_ce_key_paths(directory_path)); + // Continue on error. + } + s_deferred_fixations.clear(); +} + std::vector fscrypt_get_unlocked_users() { std::vector user_ids; for (const auto& it : s_ce_policies) { diff --git a/FsCrypt.h b/FsCrypt.h index 2cb47eb..8d84bdc 100644 --- a/FsCrypt.h +++ b/FsCrypt.h @@ -26,6 +26,7 @@ extern bool fscrypt_init_user0_done; 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_set_user_key_protection(userid_t user_id, const std::string& secret); +void fscrypt_deferred_fixate_ce_keys(); std::vector fscrypt_get_unlocked_users(); bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& secret); diff --git a/KeyStorage.cpp b/KeyStorage.cpp index eb994e9..33d415e 100644 --- a/KeyStorage.cpp +++ b/KeyStorage.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -231,9 +230,8 @@ static bool CommitUpgradedKey(Keystore& keystore, const std::string& dir) { return true; } -static void DeferredCommitKeys() { - android::base::WaitForProperty("vold.checkpoint_committed", "1"); - LOG(INFO) << "Committing upgraded keys"; +void DeferredCommitKeystoreKeys() { + LOG(INFO) << "Committing upgraded Keystore keys"; Keystore keystore; if (!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 lock(key_upgrade_lock); for (auto& dir : key_dirs_to_commit) { - LOG(INFO) << "Committing upgraded key " << dir; + LOG(INFO) << "Committing upgraded Keystore key for " << dir; CommitUpgradedKey(keystore, dir); } 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 @@ -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 // directory. static void ScheduleKeyCommit(const std::string& dir) { - if (key_dirs_to_commit.empty()) std::thread(DeferredCommitKeys).detach(); key_dirs_to_commit.push_back(dir); } diff --git a/KeyStorage.h b/KeyStorage.h index cc2f549..22453ea 100644 --- a/KeyStorage.h +++ b/KeyStorage.h @@ -41,6 +41,8 @@ extern const KeyAuthentication kEmptyAuthentication; bool createSecdiscardable(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. // 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); From a3594a6030580e195946e064c7c6711b7b8b4dc0 Mon Sep 17 00:00:00 2001 From: Paul Lawrence Date: Tue, 31 Jan 2023 13:42:09 -0800 Subject: [PATCH 08/11] Check bounds on dm-bow checkpoint restore Perform bounds checks on the dm-bow restore log sectors Bug: 204449591 Test: atest vts_kernel_checkpoint_test passes Ignore-AOSP-First: Security fix Change-Id: Ie4776581ce1aff5a1a6beb9140dc6a38a9c8dfdb --- Checkpoint.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/Checkpoint.cpp b/Checkpoint.cpp index 7ff7766..eca49ef 100644 --- a/Checkpoint.cpp +++ b/Checkpoint.cpp @@ -597,21 +597,31 @@ void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector& // Read from the device // 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 relocatedRead(int device_fd, Relocations const& relocations, bool validating, sector_t sector, uint32_t size, uint32_t block_size) { if (!validating) { std::vector buffer(size); - lseek64(device_fd, sector * kSectorSize, SEEK_SET); - read(device_fd, &buffer[0], size); + off64_t offset = sector * kSectorSize; + if (lseek64(device_fd, offset, SEEK_SET) != offset) { + return std::vector(); + } + if (read(device_fd, &buffer[0], size) != static_cast(size)) { + return std::vector(); + } return buffer; } std::vector buffer(size); for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) { auto relocation = --relocations.upper_bound(sector); - lseek64(device_fd, (sector + relocation->second - relocation->first) * kSectorSize, - SEEK_SET); - read(device_fd, &buffer[i], block_size); + off64_t offset = (sector + relocation->second - relocation->first) * kSectorSize; + if (lseek64(device_fd, offset, SEEK_SET) != offset) { + return std::vector(); + } + if (read(device_fd, &buffer[i], block_size) != static_cast(block_size)) { + return std::vector(); + } } return buffer; @@ -634,7 +644,10 @@ Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) { if (device_fd < 0) return error("Cannot open " + blockDevice); log_sector_v1_0 original_ls; - read(device_fd, reinterpret_cast(&original_ls), sizeof(original_ls)); + if (read(device_fd, reinterpret_cast(&original_ls), sizeof(original_ls)) != + sizeof(original_ls)) { + return error(EINVAL, "Cannot read sector"); + } if (original_ls.magic == kPartialRestoreMagic) { validating = false; action = "Restoring"; @@ -642,11 +655,19 @@ Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) { 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"; for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) { auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0, 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(&ls_buffer[0]); Used_Sectors used_sectors; @@ -668,6 +689,14 @@ Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) { 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; for (log_entry* le = reinterpret_cast(&ls_buffer[ls.header_size]) + ls.count - 1; @@ -677,8 +706,16 @@ Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) { << " to " << le->source << " with checksum " << std::hex << 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, 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); for (size_t i = 0; i < le->size; i += ls.block_size) { crc32(&buffer[i], ls.block_size, &checksum); @@ -711,8 +748,17 @@ Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) { LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward"; auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0, original_ls.block_size, original_ls.block_size); - lseek64(device_fd, 0, SEEK_SET); - write(device_fd, &buffer[0], original_ls.block_size); + if (buffer.size() != 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(original_ls.block_size)) { + return error(EINVAL, "Failed to write original sector"); + } return Status::ok(); } From f39c44a093e5c60be3c7dece0ebfe451ab273d62 Mon Sep 17 00:00:00 2001 From: Alfred Piccioni Date: Thu, 2 Feb 2023 09:46:30 +0000 Subject: [PATCH 09/11] Revert "Add NTFS support in vold" Ignore-AOSP-First: Change already merged in AOSP, has same change ID, so manually merging into master. This reverts commit 564f6c649a6cbcc34fec65b19f31a978709ba210. Reason for revert: Un-backporting. Note: This is not a direct revert. We should keep the minor refactoring in PublicVolume.cpp; no point making the code worse. Test: Revert. Change-Id: I68e31bd55158ea06af4f42f14723d3404d63f6d5 --- Android.bp | 1 - fs/Ntfs.cpp | 134 ----------------------------------------- fs/Ntfs.h | 39 ------------ main.cpp | 3 +- model/PublicVolume.cpp | 19 +----- 5 files changed, 2 insertions(+), 194 deletions(-) delete mode 100644 fs/Ntfs.cpp delete mode 100644 fs/Ntfs.h diff --git a/Android.bp b/Android.bp index 8f5cae3..1ccfc09 100644 --- a/Android.bp +++ b/Android.bp @@ -138,7 +138,6 @@ cc_library_static { "fs/Ext4.cpp", "fs/F2fs.cpp", "fs/Vfat.cpp", - "fs/Ntfs.cpp", "model/Disk.cpp", "model/EmulatedVolume.cpp", "model/ObbVolume.cpp", diff --git a/fs/Ntfs.cpp b/fs/Ntfs.cpp deleted file mode 100644 index 6f57693..0000000 --- a/fs/Ntfs.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#define LOG_TAG "Vold" - -#include -#include -#include -#include -#include - -#include - -#include "Ntfs.h" -#include "Utils.h" -#include "VoldUtil.h" - -using android::base::StringPrintf; - -namespace android { -namespace vold { -namespace ntfs { - -static const char* kFsckPath = "/system/bin/ntfsfix"; -static const char* kMkfsPath = "/system/bin/mkntfs"; - -static const char* fsName = "ntfs3"; - -bool IsSupported() { - return access(kFsckPath, X_OK) == 0 && access(kMkfsPath, X_OK) == 0 && - IsFilesystemSupported(fsName); -} - -status_t Check(const std::string& source) { - std::vector cmd; - cmd.push_back(kFsckPath); - - // ntfsfix sets the dirty bit by default, which prevents mounting the drive. - // -d tells it to instead reset the dirty bit. Technically, this could be dangerous, - // but since ntfsfix should report any errors with the drive and separately return - // a failed check, this should be relatively safe. - cmd.push_back("-d"); - - cmd.push_back(source); - - int rc = ForkExecvpTimeout(cmd, kUntrustedFsckSleepTime, sFsckUntrustedContext); - if (rc == 0) { - LOG(INFO) << "Check NTFS OK"; - return 0; - } else { - LOG(ERROR) << "Check NTFS failed (code " << rc << ")"; - errno = EIO; - return -1; - } -} - -status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount, - bool executable, int ownerUid, int ownerGid, int permMask) { - unsigned long flags = MS_NODEV | MS_NOSUID | MS_DIRSYNC | MS_NOATIME; - - flags |= (executable ? 0 : MS_NOEXEC); - flags |= (ro ? MS_RDONLY : 0); - flags |= (remount ? MS_REMOUNT : 0); - - // Android mount does not support "utf8" as an option. We use the deprecated iocharset instead. - auto mountData = android::base::StringPrintf("uid=%d,gid=%d,fmask=%o,dmask=%o,iocharset=%s", - ownerUid, ownerGid, permMask, permMask, "utf8"); - - int rc = mount(source.c_str(), target.c_str(), fsName, flags, mountData.c_str()); - - if (rc && errno == EROFS) { - LOG(ERROR) << source << " appears to be a read only filesystem - retrying mount RO"; - flags |= MS_RDONLY; - rc = mount(source.c_str(), target.c_str(), fsName, flags, mountData.c_str()); - } - - return rc; -} - -status_t Format(const std::string& source, unsigned int numSectors) { - std::vector cmd; - cmd.push_back(kMkfsPath); - cmd.push_back(source); - - if (numSectors) { - cmd.push_back(StringPrintf("%u", numSectors)); - } - - int rc = ForkExecvp(cmd); - if (rc == 0) { - LOG(INFO) << "Filesystem formatted OK"; - return 0; - } else { - LOG(ERROR) << "Format failed with error code: " << rc; - errno = EIO; - return -1; - } - return 0; -} - -} // namespace ntfs -} // namespace vold -} // namespace android \ No newline at end of file diff --git a/fs/Ntfs.h b/fs/Ntfs.h deleted file mode 100644 index 0049490..0000000 --- a/fs/Ntfs.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_VOLD_NTFS_H -#define ANDROID_VOLD_NTFS_H - -#include - -#include - -namespace android { -namespace vold { -namespace ntfs { - -bool IsSupported(); - -status_t Check(const std::string& source); -status_t Mount(const std::string& source, const std::string& target, bool ro, bool remount, - bool executable, int ownerUid, int ownerGid, int permMask); -status_t Format(const std::string& source, unsigned int numSectors); - -} // namespace ntfs -} // namespace vold -} // namespace android - -#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index 50a02ae..b07ee68 100644 --- a/main.cpp +++ b/main.cpp @@ -74,8 +74,7 @@ int main(int argc, char** argv) { LOG(DEBUG) << "Detected support for:" << (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "") << (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "") - << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "") - << (android::vold::IsFilesystemSupported("ntfs3") ? " ntfs3" : ""); + << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : ""); VolumeManager* vm; NetlinkManager* nm; diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp index 20015af..b13a0ea 100644 --- a/model/PublicVolume.cpp +++ b/model/PublicVolume.cpp @@ -20,7 +20,6 @@ #include "Utils.h" #include "VolumeManager.h" #include "fs/Exfat.h" -#include "fs/Ntfs.h" #include "fs/Vfat.h" #include @@ -111,11 +110,6 @@ status_t PublicVolume::doMount() { LOG(ERROR) << getId() << " failed filesystem check"; return -EIO; } - } else if (mFsType == "ntfs" && ntfs::IsSupported()) { - if (ntfs::Check(mDevPath)) { - LOG(ERROR) << getId() << " failed filesystem check"; - return -EIO; - } } else { LOG(ERROR) << getId() << " unsupported filesystem " << mFsType; return -EIO; @@ -158,12 +152,6 @@ status_t PublicVolume::doMount() { PLOG(ERROR) << getId() << " failed to mount " << mDevPath; return -EIO; } - } else if (mFsType == "ntfs") { - if (ntfs::Mount(mDevPath, mRawPath, false, false, false, AID_ROOT, - (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE), 0007)) { - PLOG(ERROR) << getId() << " failed to mount " << mDevPath; - return -EIO; - } } if (getMountFlags() & MountFlags::kPrimary) { @@ -322,10 +310,9 @@ status_t PublicVolume::doUnmount() { status_t PublicVolume::doFormat(const std::string& fsType) { bool isVfatSup = vfat::IsSupported(); bool isExfatSup = exfat::IsSupported(); - bool isNtfsSup = ntfs::IsSupported(); status_t res = OK; - enum { NONE, VFAT, EXFAT, NTFS } fsPick = NONE; + enum { NONE, VFAT, EXFAT } fsPick = NONE; // Resolve auto requests if (fsType == "auto" && isVfatSup && isExfatSup) { @@ -354,8 +341,6 @@ status_t PublicVolume::doFormat(const std::string& fsType) { fsPick = VFAT; } else if (fsType == "exfat" && isExfatSup) { fsPick = EXFAT; - } else if (fsType == "ntfs" && isNtfsSup) { - fsPick = NTFS; } if (WipeBlockDevice(mDevPath) != OK) { @@ -366,8 +351,6 @@ status_t PublicVolume::doFormat(const std::string& fsType) { res = vfat::Format(mDevPath, 0); } else if (fsPick == EXFAT) { res = exfat::Format(mDevPath); - } else if (fsPick == NTFS) { - res = ntfs::Format(mDevPath, 0); } else { LOG(ERROR) << "Unsupported filesystem " << fsType; return -EINVAL; From 7278162512fc1c78bc09ab132c7d9bd7f7c8a397 Mon Sep 17 00:00:00 2001 From: Paul Lawrence Date: Wed, 1 Mar 2023 14:32:46 -0800 Subject: [PATCH 10/11] Use kernel sys/fs/fuse/features/fuse_bpf flag to enable fuse_bpf Bug: 262887267 Test: ro.fuse.bpf.is_running is true Ignore-AOSP-First: This breaks multiple tests because the rest of fuse_bpf is not in aosp Change-Id: I3c41c7a0992803a100a1a7eeecd2c5f57e6085fd --- Utils.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Utils.cpp b/Utils.cpp index 157b839..bcde4d2 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -1772,15 +1772,18 @@ std::pair OpenDirInProcfs(std::string_vie } bool IsFuseBpfEnabled() { - // TODO Once kernel supports flag, trigger off kernel flag unless - // ro.fuse.bpf.enabled is explicitly set to false bool enabled; + std::string contents; + 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 + else if (base::GetProperty("ro.fuse.bpf.enabled", "") != "") enabled = base::GetBoolProperty("ro.fuse.bpf.enabled", false); + else + enabled = base::ReadFileToString("/sys/fs/fuse/features/fuse_bpf", &contents) && + contents == "supported\n"; if (enabled) { base::SetProperty("ro.fuse.bpf.is_running", "true"); From 9adf86a88189af16e04e8b439bd2e5078acadb4d Mon Sep 17 00:00:00 2001 From: Paul Lawrence Date: Fri, 14 Apr 2023 10:06:49 -0700 Subject: [PATCH 11/11] Log reason for fuse-bpf being enabled/disabled Also don't try to set ro.fuse.bpf.is_running if it's already set. Bug: 278263648 Ignore-AOSP-First: Feature is in internal Test: Examine logs with various properties set Change-Id: I9623a02c7065fa0d0c71c618d448bda0cd2a900e --- Utils.cpp | 52 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/Utils.cpp b/Utils.cpp index bcde4d2..40a182b 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -1771,27 +1771,45 @@ std::pair OpenDirInProcfs(std::string_vie return {std::move(fd), std::move(linkPath)}; } +static bool IsPropertySet(const char* name, bool& value) { + if (base::GetProperty(name, "") == "") return false; + + value = base::GetBoolProperty(name, false); + LOG(INFO) << "fuse-bpf is " << (value ? "enabled" : "disabled") << " because of property " + << name; + return true; +} + bool IsFuseBpfEnabled() { - bool enabled; - std::string contents; + // This logic is reproduced in packages/providers/MediaProvider/jni/FuseDaemon.cpp + // so changes made here must be reflected there + bool enabled = false; - 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 if (base::GetProperty("ro.fuse.bpf.enabled", "") != "") - enabled = base::GetBoolProperty("ro.fuse.bpf.enabled", false); - else - enabled = base::ReadFileToString("/sys/fs/fuse/features/fuse_bpf", &contents) && - contents == "supported\n"; + if (IsPropertySet("ro.fuse.bpf.is_running", enabled)) return enabled; - if (enabled) { - base::SetProperty("ro.fuse.bpf.is_running", "true"); - return true; - } else { - base::SetProperty("ro.fuse.bpf.is_running", "false"); - return false; + 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