Move encrypted directories into place already-encrypted

Even after having changed the SELinux policy to remove system_server's
permission to create directories like /data/system_ce/10, there's still
a very small loophole where system_server can create a subdirectory
after vold creates the directory but before vold assigns an encryption
policy to it.  This isn't known to have actually happened (b/285239971
was a candidate, but it seems to have actually been caused by SELinux
being in permissive mode), but it's theoretically possible.

Close this loophole by making vold create encrypted directories under
temporary names and move them into place once they are fully prepared.

Bug: 156305599
Bug: 285239971
Test: Cuttlefish boots, and can be rebooted.
Change-Id: I53407c938bab02ab4b7e5bab8402f36eb47fb203
This commit is contained in:
Eric Biggers 2023-06-08 16:15:52 +00:00
parent 39f11368a5
commit c6f004a9c4

View file

@ -323,8 +323,37 @@ static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gi
// 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 (IsFbeEnabled() && !EnsurePolicy(policy, dir)) return false;
if (android::vold::pathExists(dir)) {
if (!prepare_dir(dir, mode, uid, gid)) return false;
if (IsFbeEnabled() && !EnsurePolicy(policy, dir)) return false;
} else {
// If the directory does not yet exist, then create it under a temporary name, and only move
// it to the final name after it is fully prepared with an encryption policy and the desired
// file permissions. This prevents the directory from being accessed before it is ready.
//
// Note: this relies on the SELinux file_contexts assigning the same type to the file path
// with the ".new" suffix as to the file path without the ".new" suffix.
const std::string tmp_dir = dir + ".new";
if (android::vold::pathExists(tmp_dir)) {
android::vold::DeleteDirContentsAndDir(tmp_dir);
}
if (!prepare_dir(tmp_dir, mode, uid, gid)) return false;
if (IsFbeEnabled() && !EnsurePolicy(policy, tmp_dir)) return false;
// On some buggy kernels, renaming a directory that is both encrypted and case-insensitive
// fails in some specific circumstances. Unfortunately, these circumstances happen here
// when processing the "media" directory. This was already fixed by kernel commit
// https://git.kernel.org/linus/b5639bb4313b9d45 ('f2fs: don't use casefolded comparison for
// "." and ".."'). But to support kernels that lack that fix, we use the below workaround.
// It bypasses the bug by making the encryption key of tmp_dir be loaded before the rename.
android::vold::pathExists(tmp_dir + "/subdir");
if (rename(tmp_dir.c_str(), dir.c_str()) != 0) {
PLOG(ERROR) << "Failed to rename " << tmp_dir << " to " << dir;
return false;
}
}
return true;
}