diff options
Diffstat (limited to 'libfscrypt')
l--------- | libfscrypt/.clang-format | 1 | ||||
-rw-r--r-- | libfscrypt/Android.bp | 1 | ||||
-rw-r--r-- | libfscrypt/fscrypt.cpp | 427 | ||||
-rw-r--r-- | libfscrypt/fscrypt_init_extensions.cpp | 146 | ||||
-rw-r--r-- | libfscrypt/include/fscrypt/fscrypt.h | 41 | ||||
-rw-r--r-- | libfscrypt/include/fscrypt/fscrypt_init_extensions.h | 33 | ||||
-rw-r--r-- | libfscrypt/tests/Android.bp | 33 | ||||
-rw-r--r-- | libfscrypt/tests/fscrypt_test.cpp | 138 |
8 files changed, 376 insertions, 444 deletions
diff --git a/libfscrypt/.clang-format b/libfscrypt/.clang-format deleted file mode 120000 index 973b2fab..00000000 --- a/libfscrypt/.clang-format +++ /dev/null @@ -1 +0,0 @@ -../../../build/soong/scripts/system-clang-format
\ No newline at end of file diff --git a/libfscrypt/Android.bp b/libfscrypt/Android.bp index 65b94ed9..cca38235 100644 --- a/libfscrypt/Android.bp +++ b/libfscrypt/Android.bp @@ -5,6 +5,7 @@ cc_library { recovery_available: true, srcs: [ "fscrypt.cpp", + "fscrypt_init_extensions.cpp", ], export_include_dirs: ["include"], shared_libs: [ diff --git a/libfscrypt/fscrypt.cpp b/libfscrypt/fscrypt.cpp index b76f0b17..adeb66aa 100644 --- a/libfscrypt/fscrypt.cpp +++ b/libfscrypt/fscrypt.cpp @@ -16,101 +16,42 @@ #include "fscrypt/fscrypt.h" -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/strings.h> -#include <android-base/unique_fd.h> +#include <array> + #include <asm/ioctl.h> -#include <cutils/properties.h> +#include <dirent.h> #include <errno.h> #include <fcntl.h> #include <linux/fs.h> -#include <logwrap/logwrap.h> #include <string.h> #include <sys/stat.h> +#include <sys/syscall.h> #include <sys/types.h> #include <unistd.h> -#include <utils/misc.h> - -#include <array> -#include <string> -#include <vector> - -using namespace std::string_literals; - -// TODO: switch to <linux/fscrypt.h> once it's in Bionic -#ifndef FSCRYPT_POLICY_V1 -// Careful: due to an API quirk this is actually 0, not 1. We use 1 everywhere -// else, so make sure to only use this constant in the ioctl itself. -#define FSCRYPT_POLICY_V1 0 -#define FSCRYPT_KEY_DESCRIPTOR_SIZE 8 -struct fscrypt_policy_v1 { - __u8 version; - __u8 contents_encryption_mode; - __u8 filenames_encryption_mode; - __u8 flags; - __u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE]; -}; - -#define FSCRYPT_POLICY_V2 2 -#define FSCRYPT_KEY_IDENTIFIER_SIZE 16 -struct fscrypt_policy_v2 { - __u8 version; - __u8 contents_encryption_mode; - __u8 filenames_encryption_mode; - __u8 flags; - __u8 __reserved[4]; - __u8 master_key_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]; -}; +#include <android-base/file.h> +#include <android-base/logging.h> +#include <cutils/properties.h> +#include <logwrap/logwrap.h> +#include <utils/misc.h> -#endif /* FSCRYPT_POLICY_V1 */ +#define FS_KEY_DESCRIPTOR_SIZE_HEX (2 * FS_KEY_DESCRIPTOR_SIZE + 1) /* modes not supported by upstream kernel, so not in <linux/fs.h> */ #define FS_ENCRYPTION_MODE_AES_256_HEH 126 #define FS_ENCRYPTION_MODE_PRIVATE 127 -#define HEX_LOOKUP "0123456789abcdef" - -struct ModeLookupEntry { - std::string name; - int id; -}; - -static const auto contents_modes = std::vector<ModeLookupEntry>{ - {"aes-256-xts"s, FS_ENCRYPTION_MODE_AES_256_XTS}, - {"software"s, FS_ENCRYPTION_MODE_AES_256_XTS}, - {"adiantum"s, FS_ENCRYPTION_MODE_ADIANTUM}, - {"ice"s, FS_ENCRYPTION_MODE_PRIVATE}, -}; +/* new definition, not yet in Bionic's <linux/fs.h> */ +#ifndef FS_ENCRYPTION_MODE_ADIANTUM +#define FS_ENCRYPTION_MODE_ADIANTUM 9 +#endif -static const auto filenames_modes = std::vector<ModeLookupEntry>{ - {"aes-256-cts"s, FS_ENCRYPTION_MODE_AES_256_CTS}, - {"aes-256-heh"s, FS_ENCRYPTION_MODE_AES_256_HEH}, - {"adiantum"s, FS_ENCRYPTION_MODE_ADIANTUM}, -}; - -static bool LookupModeByName(const std::vector<struct ModeLookupEntry>& modes, - const std::string& name, int* result) { - for (const auto& e : modes) { - if (e.name == name) { - *result = e.id; - return true; - } - } - return false; -} +/* new definition, not yet in Bionic's <linux/fs.h> */ +#ifndef FS_POLICY_FLAG_DIRECT_KEY +#define FS_POLICY_FLAG_DIRECT_KEY 0x4 +#endif -static bool LookupModeById(const std::vector<struct ModeLookupEntry>& modes, int id, - std::string* result) { - for (const auto& e : modes) { - if (e.id == id) { - *result = e.name; - return true; - } - } - return false; -} +#define HEX_LOOKUP "0123456789abcdef" bool fscrypt_is_native() { char value[PROPERTY_VALUE_MAX]; @@ -118,14 +59,11 @@ bool fscrypt_is_native() { return !strcmp(value, "file"); } -namespace android { -namespace fscrypt { - static void log_ls(const char* dirname) { std::array<const char*, 3> argv = {"ls", "-laZ", dirname}; int status = 0; auto res = - logwrap_fork_execvp(argv.size(), argv.data(), &status, false, LOG_ALOG, false, nullptr); + android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), &status, false, true); if (res != 0) { PLOG(ERROR) << argv[0] << " " << argv[1] << " " << argv[2] << "failed"; return; @@ -142,197 +80,204 @@ static void log_ls(const char* dirname) { } } -void BytesToHex(const std::string& bytes, std::string* hex) { - hex->clear(); - for (char c : bytes) { - *hex += HEX_LOOKUP[(c & 0xF0) >> 4]; - *hex += HEX_LOOKUP[c & 0x0F]; +static void policy_to_hex(const char* policy, char* hex) { + for (size_t i = 0, j = 0; i < FS_KEY_DESCRIPTOR_SIZE; i++) { + hex[j++] = HEX_LOOKUP[(policy[i] & 0xF0) >> 4]; + hex[j++] = HEX_LOOKUP[policy[i] & 0x0F]; } + hex[FS_KEY_DESCRIPTOR_SIZE_HEX - 1] = '\0'; } -static bool fscrypt_is_encrypted(int fd) { - fscrypt_policy_v1 policy; - - // success => encrypted with v1 policy - // EINVAL => encrypted with v2 policy - // ENODATA => not encrypted - return ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) == 0 || errno == EINVAL; +static bool is_dir_empty(const char *dirname, bool *is_empty) +{ + int n = 0; + auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(dirname), closedir); + if (!dirp) { + PLOG(ERROR) << "Unable to read directory: " << dirname; + return false; + } + for (;;) { + errno = 0; + auto entry = readdir(dirp.get()); + if (!entry) { + if (errno) { + PLOG(ERROR) << "Unable to read directory: " << dirname; + return false; + } + break; + } + if (strcmp(entry->d_name, "lost+found") != 0) { // Skip lost+found + ++n; + if (n > 2) { + *is_empty = false; + return true; + } + } + } + *is_empty = true; + return true; } -bool OptionsToString(const EncryptionOptions& options, std::string* options_string) { - std::string contents_mode, filenames_mode; - if (!LookupModeById(contents_modes, options.contents_mode, &contents_mode)) { - return false; +static uint8_t fscrypt_get_policy_flags(int filenames_encryption_mode) { + if (filenames_encryption_mode == FS_ENCRYPTION_MODE_AES_256_CTS) { + // Use legacy padding with our original filenames encryption mode. + return FS_POLICY_FLAGS_PAD_4; + } else if (filenames_encryption_mode == FS_ENCRYPTION_MODE_ADIANTUM) { + // Use DIRECT_KEY for Adiantum, since it's much more efficient but just + // as secure since Android doesn't reuse the same master key for + // multiple encryption modes + return (FS_POLICY_FLAGS_PAD_16 | FS_POLICY_FLAG_DIRECT_KEY); } - if (!LookupModeById(filenames_modes, options.filenames_mode, &filenames_mode)) { + // With a new mode we can use the better padding flag without breaking existing devices: pad + // filenames with zeroes to the next 16-byte boundary. This is more secure (helps hide the + // length of filenames) and makes the inputs evenly divisible into blocks which is more + // efficient for encryption and decryption. + return FS_POLICY_FLAGS_PAD_16; +} + +static bool fscrypt_policy_set(const char *directory, const char *policy, + size_t policy_length, + int contents_encryption_mode, + int filenames_encryption_mode) { + if (policy_length != FS_KEY_DESCRIPTOR_SIZE) { + LOG(ERROR) << "Policy wrong length: " << policy_length; return false; } - *options_string = contents_mode + ":" + filenames_mode + ":v" + std::to_string(options.version); - if ((options.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64)) { - *options_string += "+inlinecrypt_optimized"; - } - EncryptionOptions options_check; - if (!ParseOptions(*options_string, &options_check)) { - LOG(ERROR) << "Internal error serializing options as string: " << *options_string; + char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(policy, policy_hex); + + int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (fd == -1) { + PLOG(ERROR) << "Failed to open directory " << directory; return false; } - if (memcmp(&options, &options_check, sizeof(options_check)) != 0) { - LOG(ERROR) << "Internal error serializing options as string, round trip failed: " - << *options_string; + + fscrypt_policy fp; + fp.version = 0; + fp.contents_encryption_mode = contents_encryption_mode; + fp.filenames_encryption_mode = filenames_encryption_mode; + fp.flags = fscrypt_get_policy_flags(filenames_encryption_mode); + memcpy(fp.master_key_descriptor, policy, FS_KEY_DESCRIPTOR_SIZE); + if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, &fp)) { + PLOG(ERROR) << "Failed to set encryption policy for " << directory << " to " << policy_hex + << " modes " << contents_encryption_mode << "/" << filenames_encryption_mode; + close(fd); return false; } + close(fd); + + LOG(INFO) << "Policy for " << directory << " set to " << policy_hex + << " modes " << contents_encryption_mode << "/" << filenames_encryption_mode; return true; } -bool ParseOptions(const std::string& options_string, EncryptionOptions* options) { - memset(options, '\0', sizeof(*options)); - auto parts = android::base::Split(options_string, ":"); - if (parts.size() < 1 || parts.size() > 3) { +static bool fscrypt_policy_get(const char *directory, char *policy, + size_t policy_length, + int contents_encryption_mode, + int filenames_encryption_mode) { + if (policy_length != FS_KEY_DESCRIPTOR_SIZE) { + LOG(ERROR) << "Policy wrong length: " << policy_length; return false; } - if (!LookupModeByName(contents_modes, parts[0], &options->contents_mode)) { - LOG(ERROR) << "Invalid file contents encryption mode: " << parts[0]; + + int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (fd == -1) { + PLOG(ERROR) << "Failed to open directory " << directory; return false; } - if (parts.size() >= 2) { - if (!LookupModeByName(filenames_modes, parts[1], &options->filenames_mode)) { - LOG(ERROR) << "Invalid file names encryption mode: " << parts[1]; - return false; - } - } else if (options->contents_mode == FS_ENCRYPTION_MODE_ADIANTUM) { - options->filenames_mode = FS_ENCRYPTION_MODE_ADIANTUM; - } else { - options->filenames_mode = FS_ENCRYPTION_MODE_AES_256_CTS; - } - options->version = 1; - options->flags = 0; - if (parts.size() >= 3) { - auto flags = android::base::Split(parts[2], "+"); - for (const auto& flag : flags) { - if (flag == "v1") { - options->version = 1; - } else if (flag == "v2") { - options->version = 2; - } else if (flag == "inlinecrypt_optimized") { - options->flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64; - } else { - LOG(ERROR) << "Unknown flag: " << flag; - return false; - } - } - } - // In the original setting of v1 policies and AES-256-CTS we used 4-byte - // padding of filenames, so we have to retain that for compatibility. - // - // For everything else, use 16-byte padding. This is more secure (it helps - // hide the length of filenames), and it makes the inputs evenly divisible - // into cipher blocks which is more efficient for encryption and decryption. - if (options->version == 1 && options->filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) { - options->flags |= FS_POLICY_FLAGS_PAD_4; - } else { - options->flags |= FS_POLICY_FLAGS_PAD_16; + fscrypt_policy fp; + memset(&fp, 0, sizeof(fscrypt_policy)); + if (ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &fp) != 0) { + PLOG(ERROR) << "Failed to get encryption policy for " << directory; + close(fd); + log_ls(directory); + return false; } - - // Use DIRECT_KEY for Adiantum, since it's much more efficient but just as - // secure since Android doesn't reuse the same master key for multiple - // encryption modes. - if (options->filenames_mode == FS_ENCRYPTION_MODE_ADIANTUM) { - options->flags |= FS_POLICY_FLAG_DIRECT_KEY; + close(fd); + + if ((fp.version != 0) + || (fp.contents_encryption_mode != contents_encryption_mode) + || (fp.filenames_encryption_mode != filenames_encryption_mode) + || (fp.flags != + fscrypt_get_policy_flags(filenames_encryption_mode))) { + LOG(ERROR) << "Failed to find matching encryption policy for " << directory; + return false; } - return true; -} + memcpy(policy, fp.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE); -static std::string PolicyDebugString(const EncryptionPolicy& policy) { - std::stringstream ss; - std::string ref_hex; - BytesToHex(policy.key_raw_ref, &ref_hex); - ss << ref_hex; - ss << " v" << policy.options.version; - ss << " modes " << policy.options.contents_mode << "/" << policy.options.filenames_mode; - ss << std::hex << " flags 0x" << policy.options.flags; - return ss.str(); + return true; } -bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory) { - union { - fscrypt_policy_v1 v1; - fscrypt_policy_v2 v2; - } kern_policy; - memset(&kern_policy, 0, sizeof(kern_policy)); - - switch (policy.options.version) { - case 1: - if (policy.key_raw_ref.size() != FSCRYPT_KEY_DESCRIPTOR_SIZE) { - LOG(ERROR) << "Invalid key descriptor length for v1 policy: " - << policy.key_raw_ref.size(); - return false; - } - // Careful: FSCRYPT_POLICY_V1 is actually 0 in the API, so make sure - // to use it here instead of a literal 1. - kern_policy.v1.version = FSCRYPT_POLICY_V1; - kern_policy.v1.contents_encryption_mode = policy.options.contents_mode; - kern_policy.v1.filenames_encryption_mode = policy.options.filenames_mode; - kern_policy.v1.flags = policy.options.flags; - policy.key_raw_ref.copy(reinterpret_cast<char*>(kern_policy.v1.master_key_descriptor), - FSCRYPT_KEY_DESCRIPTOR_SIZE); - break; - case 2: - if (policy.key_raw_ref.size() != FSCRYPT_KEY_IDENTIFIER_SIZE) { - LOG(ERROR) << "Invalid key identifier length for v2 policy: " - << policy.key_raw_ref.size(); - return false; - } - kern_policy.v2.version = FSCRYPT_POLICY_V2; - kern_policy.v2.contents_encryption_mode = policy.options.contents_mode; - kern_policy.v2.filenames_encryption_mode = policy.options.filenames_mode; - kern_policy.v2.flags = policy.options.flags; - policy.key_raw_ref.copy(reinterpret_cast<char*>(kern_policy.v2.master_key_identifier), - FSCRYPT_KEY_IDENTIFIER_SIZE); - break; - default: - LOG(ERROR) << "Invalid encryption policy version: " << policy.options.version; - return false; +static bool fscrypt_policy_check(const char *directory, const char *policy, + size_t policy_length, + int contents_encryption_mode, + int filenames_encryption_mode) { + if (policy_length != FS_KEY_DESCRIPTOR_SIZE) { + LOG(ERROR) << "Policy wrong length: " << policy_length; + return false; } - - android::base::unique_fd fd(open(directory.c_str(), O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)); - if (fd == -1) { - PLOG(ERROR) << "Failed to open directory " << directory; + char existing_policy[FS_KEY_DESCRIPTOR_SIZE]; + if (!fscrypt_policy_get(directory, existing_policy, FS_KEY_DESCRIPTOR_SIZE, + contents_encryption_mode, + filenames_encryption_mode)) return false; + char existing_policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + + policy_to_hex(existing_policy, existing_policy_hex); + + if (memcmp(policy, existing_policy, FS_KEY_DESCRIPTOR_SIZE) != 0) { + char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(policy, policy_hex); + LOG(ERROR) << "Found policy " << existing_policy_hex << " at " << directory + << " which doesn't match expected value " << policy_hex; + log_ls(directory); return false; } + LOG(INFO) << "Found policy " << existing_policy_hex << " at " << directory + << " which matches expected value"; + return true; +} - bool already_encrypted = fscrypt_is_encrypted(fd); +int fscrypt_policy_ensure(const char *directory, const char *policy, + size_t policy_length, + const char *contents_encryption_mode, + const char *filenames_encryption_mode) { + int contents_mode = 0; + int filenames_mode = 0; + + if (!strcmp(contents_encryption_mode, "software") || + !strcmp(contents_encryption_mode, "aes-256-xts")) { + contents_mode = FS_ENCRYPTION_MODE_AES_256_XTS; + } else if (!strcmp(contents_encryption_mode, "adiantum")) { + contents_mode = FS_ENCRYPTION_MODE_ADIANTUM; + } else if (!strcmp(contents_encryption_mode, "ice")) { + contents_mode = FS_ENCRYPTION_MODE_PRIVATE; + } else { + LOG(ERROR) << "Invalid file contents encryption mode: " + << contents_encryption_mode; + return -1; + } - // FS_IOC_SET_ENCRYPTION_POLICY will set the policy if the directory is - // unencrypted; otherwise it will verify that the existing policy matches. - // Setting the policy will fail if the directory is already nonempty. - if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, &kern_policy) != 0) { - std::string reason; - switch (errno) { - case EEXIST: - reason = "The directory already has a different encryption policy."; - break; - default: - reason = strerror(errno); - break; - } - LOG(ERROR) << "Failed to set encryption policy of " << directory << " to " - << PolicyDebugString(policy) << ": " << reason; - if (errno == ENOTEMPTY) { - log_ls(directory.c_str()); - } - return false; + if (!strcmp(filenames_encryption_mode, "aes-256-cts")) { + filenames_mode = FS_ENCRYPTION_MODE_AES_256_CTS; + } else if (!strcmp(filenames_encryption_mode, "aes-256-heh")) { + filenames_mode = FS_ENCRYPTION_MODE_AES_256_HEH; + } else if (!strcmp(filenames_encryption_mode, "adiantum")) { + filenames_mode = FS_ENCRYPTION_MODE_ADIANTUM; + } else { + LOG(ERROR) << "Invalid file names encryption mode: " + << filenames_encryption_mode; + return -1; } - if (already_encrypted) { - LOG(INFO) << "Verified that " << directory << " has the encryption policy " - << PolicyDebugString(policy); + bool is_empty; + if (!is_dir_empty(directory, &is_empty)) return -1; + if (is_empty) { + if (!fscrypt_policy_set(directory, policy, policy_length, + contents_mode, filenames_mode)) return -1; } else { - LOG(INFO) << "Encryption policy of " << directory << " set to " - << PolicyDebugString(policy); + if (!fscrypt_policy_check(directory, policy, policy_length, + contents_mode, filenames_mode)) return -1; } - return true; + return 0; } - -} // namespace fscrypt -} // namespace android diff --git a/libfscrypt/fscrypt_init_extensions.cpp b/libfscrypt/fscrypt_init_extensions.cpp new file mode 100644 index 00000000..2fd70e79 --- /dev/null +++ b/libfscrypt/fscrypt_init_extensions.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 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 "fscrypt/fscrypt.h" +#include "fscrypt/fscrypt_init_extensions.h" + +#include <dirent.h> +#include <errno.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <cutils/properties.h> +#include <cutils/sockets.h> +#include <keyutils.h> +#include <logwrap/logwrap.h> + +#define TAG "fscrypt" + +static const std::string arbitrary_sequence_number = "42"; + +static int set_policy_on(char const* ref_basename, char const* dir); + +int fscrypt_install_keyring() +{ + key_serial_t device_keyring = add_key("keyring", "fscrypt", 0, 0, + KEY_SPEC_SESSION_KEYRING); + + if (device_keyring == -1) { + PLOG(ERROR) << "Failed to create keyring"; + return -1; + } + + LOG(INFO) << "Keyring created with id " << device_keyring << " in process " << getpid(); + + return 0; +} + +int fscrypt_set_directory_policy(const char* dir) +{ + if (!dir || strncmp(dir, "/data/", 6)) { + return 0; + } + + // Special-case /data/media/obb per b/64566063 + if (strcmp(dir, "/data/media/obb") == 0) { + // Try to set policy on this directory, but if it is non-empty this may fail. + set_policy_on(fscrypt_key_ref, dir); + return 0; + } + + // Only set policy on first level /data directories + // To make this less restrictive, consider using a policy file. + // However this is overkill for as long as the policy is simply + // to apply a global policy to all /data folders created via makedir + if (strchr(dir + 6, '/')) { + return 0; + } + + // Special case various directories that must not be encrypted, + // often because their subdirectories must be encrypted. + // This isn't a nice way to do this, see b/26641735 + std::vector<std::string> directories_to_exclude = { + "lost+found", + "system_ce", "system_de", + "misc_ce", "misc_de", + "vendor_ce", "vendor_de", + "media", + "data", "user", "user_de", + "apex", "preloads", "app-staging", + "gsi", + }; + std::string prefix = "/data/"; + for (const auto& d: directories_to_exclude) { + if ((prefix + d) == dir) { + LOG(INFO) << "Not setting policy on " << dir; + return 0; + } + } + std::vector<std::string> per_boot_directories = { + "per_boot", + }; + for (const auto& d : per_boot_directories) { + if ((prefix + d) == dir) { + LOG(INFO) << "Setting per_boot key on " << dir; + return set_policy_on(fscrypt_key_per_boot_ref, dir); + } + } + return set_policy_on(fscrypt_key_ref, dir); +} + +static int set_policy_on(char const* ref_basename, char const* dir) { + std::string ref_filename = std::string("/data") + ref_basename; + std::string policy; + if (!android::base::ReadFileToString(ref_filename, &policy)) { + LOG(ERROR) << "Unable to read system policy to set on " << dir; + return -1; + } + + auto type_filename = std::string("/data") + fscrypt_key_mode; + std::string modestring; + if (!android::base::ReadFileToString(type_filename, &modestring)) { + LOG(ERROR) << "Cannot read mode"; + } + + std::vector<std::string> modes = android::base::Split(modestring, ":"); + + if (modes.size() < 1 || modes.size() > 2) { + LOG(ERROR) << "Invalid encryption mode string: " << modestring; + return -1; + } + + LOG(INFO) << "Setting policy on " << dir; + int result = fscrypt_policy_ensure(dir, policy.c_str(), policy.length(), + modes[0].c_str(), + modes.size() >= 2 ? + modes[1].c_str() : "aes-256-cts"); + if (result) { + LOG(ERROR) << android::base::StringPrintf( + "Setting %02x%02x%02x%02x policy on %s failed!", + policy[0], policy[1], policy[2], policy[3], dir); + return -1; + } + + return 0; +} diff --git a/libfscrypt/include/fscrypt/fscrypt.h b/libfscrypt/include/fscrypt/fscrypt.h index 2b809866..ff82d47a 100644 --- a/libfscrypt/include/fscrypt/fscrypt.h +++ b/libfscrypt/include/fscrypt/fscrypt.h @@ -17,45 +17,24 @@ #ifndef _FSCRYPT_H_ #define _FSCRYPT_H_ -#include <string> +#include <sys/cdefs.h> +#include <stdbool.h> +#include <cutils/multiuser.h> -// TODO: switch to <linux/fscrypt.h> once it's in Bionic -#define FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 0x08 +__BEGIN_DECLS bool fscrypt_is_native(); +int fscrypt_policy_ensure(const char *directory, const char *policy, + size_t policy_length, + const char *contents_encryption_mode, + const char *filenames_encryption_mode); + static const char* fscrypt_unencrypted_folder = "/unencrypted"; static const char* fscrypt_key_ref = "/unencrypted/ref"; static const char* fscrypt_key_per_boot_ref = "/unencrypted/per_boot_ref"; static const char* fscrypt_key_mode = "/unencrypted/mode"; -namespace android { -namespace fscrypt { - -struct EncryptionOptions { - int version; - int contents_mode; - int filenames_mode; - int flags; - - // Ensure that "version" is not valid on creation and so must be explicitly set - EncryptionOptions() : version(0) {} -}; - -struct EncryptionPolicy { - EncryptionOptions options; - std::string key_raw_ref; -}; - -void BytesToHex(const std::string& bytes, std::string* hex); - -bool OptionsToString(const EncryptionOptions& options, std::string* options_string); - -bool ParseOptions(const std::string& options_string, EncryptionOptions* options); - -bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory); - -} // namespace fscrypt -} // namespace android +__END_DECLS #endif // _FSCRYPT_H_ diff --git a/libfscrypt/include/fscrypt/fscrypt_init_extensions.h b/libfscrypt/include/fscrypt/fscrypt_init_extensions.h new file mode 100644 index 00000000..2b6c46e1 --- /dev/null +++ b/libfscrypt/include/fscrypt/fscrypt_init_extensions.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016 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 _FSCRYPT_INIT_EXTENSIONS_H_ +#define _FSCRYPT_INIT_EXTENSIONS_H_ + +#include <sys/cdefs.h> +#include <stdbool.h> +#include <cutils/multiuser.h> + +__BEGIN_DECLS + +// These functions assume they are being called from init +// They will not operate properly outside of init +int fscrypt_install_keyring(); +int fscrypt_set_directory_policy(const char* path); + +__END_DECLS + +#endif // _FSCRYPT_INIT_EXTENSIONS_H_ diff --git a/libfscrypt/tests/Android.bp b/libfscrypt/tests/Android.bp deleted file mode 100644 index 985b425f..00000000 --- a/libfscrypt/tests/Android.bp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2019 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. - -cc_test { - name: "libfscrypt_unit_test", - - shared_libs: [ - "libbase", - ], - static_libs: [ - "libfscrypt", - ], - srcs: [ - "fscrypt_test.cpp", - ], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - ], -} diff --git a/libfscrypt/tests/fscrypt_test.cpp b/libfscrypt/tests/fscrypt_test.cpp deleted file mode 100644 index 677f0f22..00000000 --- a/libfscrypt/tests/fscrypt_test.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2019 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 <linux/fs.h> - -#include <fscrypt/fscrypt.h> - -#include <gtest/gtest.h> - -using namespace android::fscrypt; - -/* modes not supported by upstream kernel, so not in <linux/fs.h> */ -#define FS_ENCRYPTION_MODE_AES_256_HEH 126 -#define FS_ENCRYPTION_MODE_PRIVATE 127 - -TEST(fscrypt, ParseOptions) { - EncryptionOptions options; - std::string options_string; - - EXPECT_FALSE(ParseOptions("", &options)); - EXPECT_FALSE(ParseOptions("blah", &options)); - - EXPECT_TRUE(ParseOptions("software", &options)); - EXPECT_EQ(1, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("aes-256-xts:aes-256-cts:v1", options_string); - - EXPECT_TRUE(ParseOptions("aes-256-xts", &options)); - EXPECT_EQ(1, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("aes-256-xts:aes-256-cts:v1", options_string); - - EXPECT_TRUE(ParseOptions("adiantum", &options)); - EXPECT_EQ(1, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_ADIANTUM, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_ADIANTUM, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_16 | FS_POLICY_FLAG_DIRECT_KEY, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("adiantum:adiantum:v1", options_string); - - EXPECT_TRUE(ParseOptions("adiantum:aes-256-heh", &options)); - EXPECT_EQ(1, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_ADIANTUM, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_HEH, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_16, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("adiantum:aes-256-heh:v1", options_string); - - EXPECT_TRUE(ParseOptions("ice", &options)); - EXPECT_EQ(1, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_PRIVATE, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("ice:aes-256-cts:v1", options_string); - - EXPECT_FALSE(ParseOptions("ice:blah", &options)); - - EXPECT_TRUE(ParseOptions("ice:aes-256-cts", &options)); - EXPECT_EQ(1, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_PRIVATE, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("ice:aes-256-cts:v1", options_string); - - EXPECT_TRUE(ParseOptions("ice:aes-256-heh", &options)); - EXPECT_EQ(1, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_PRIVATE, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_HEH, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_16, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("ice:aes-256-heh:v1", options_string); - - EXPECT_TRUE(ParseOptions("ice:adiantum", &options)); - EXPECT_EQ(1, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_PRIVATE, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_ADIANTUM, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_16 | FS_POLICY_FLAG_DIRECT_KEY, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("ice:adiantum:v1", options_string); - - EXPECT_TRUE(ParseOptions("aes-256-xts:aes-256-cts", &options)); - EXPECT_EQ(1, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("aes-256-xts:aes-256-cts:v1", options_string); - - EXPECT_TRUE(ParseOptions("aes-256-xts:aes-256-cts:v1", &options)); - EXPECT_EQ(1, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_4, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("aes-256-xts:aes-256-cts:v1", options_string); - - EXPECT_TRUE(ParseOptions("aes-256-xts:aes-256-cts:v2", &options)); - EXPECT_EQ(2, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_16, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("aes-256-xts:aes-256-cts:v2", options_string); - - EXPECT_TRUE(ParseOptions("aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized", &options)); - EXPECT_EQ(2, options.version); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_XTS, options.contents_mode); - EXPECT_EQ(FS_ENCRYPTION_MODE_AES_256_CTS, options.filenames_mode); - EXPECT_EQ(FS_POLICY_FLAGS_PAD_16 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64, options.flags); - EXPECT_TRUE(OptionsToString(options, &options_string)); - EXPECT_EQ("aes-256-xts:aes-256-cts:v2+inlinecrypt_optimized", options_string); - - EXPECT_FALSE(ParseOptions("aes-256-xts:aes-256-cts:v2:", &options)); - EXPECT_FALSE(ParseOptions("aes-256-xts:aes-256-cts:v2:foo", &options)); - EXPECT_FALSE(ParseOptions("aes-256-xts:aes-256-cts:blah", &options)); - EXPECT_FALSE(ParseOptions("aes-256-xts:aes-256-cts:vblah", &options)); -} |