From 78efe41174950783e39782c71ff8382f851bb487 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 3 Feb 2016 15:30:33 -0700 Subject: [PATCH] Add methods to get and ensure encryption policy. The ensure call will either assign an encryption policy, or verify that an existing policy matches the expected value. Uses the new logging library so that customers can pivot logs into whatever location they want: vold into logcat and init into dmesg. Also add new directories that will have user-specific encryption policy set on them so we avoid setting the default policy. Bug: 25796509 Change-Id: Ia535630092822c80cde0939d8e46e6b47d9be2d8 --- ext4_utils/Android.mk | 3 + ext4_utils/ext4_crypt.cpp | 148 ++++++++++++++++++++---------- ext4_utils/ext4_crypt.h | 30 ++++++ ext4_utils/ext4_crypt_init_extensions.cpp | 135 +++++++++++---------------- ext4_utils/ext4_crypt_init_extensions.h | 20 +++- 5 files changed, 205 insertions(+), 131 deletions(-) create mode 100644 ext4_utils/ext4_crypt.h diff --git a/ext4_utils/Android.mk b/ext4_utils/Android.mk index 3bfab5c4..babf5ca5 100644 --- a/ext4_utils/Android.mk +++ b/ext4_utils/Android.mk @@ -66,6 +66,7 @@ LOCAL_C_INCLUDES += system/core/logwrapper/include # Various instances of dereferencing a type-punned pointer in extent.c LOCAL_CFLAGS += -fno-strict-aliasing LOCAL_SHARED_LIBRARIES := \ + libbase \ libcutils \ libext2_uuid \ libselinux \ @@ -82,6 +83,8 @@ LOCAL_MODULE := libext4_utils_static # Various instances of dereferencing a type-punned pointer in extent.c LOCAL_CFLAGS += -fno-strict-aliasing LOCAL_STATIC_LIBRARIES := \ + libbase \ + liblogwrap \ libsparse_static \ libselinux \ libbase diff --git a/ext4_utils/ext4_crypt.cpp b/ext4_utils/ext4_crypt.cpp index 4000447a..37e17c5c 100644 --- a/ext4_utils/ext4_crypt.cpp +++ b/ext4_utils/ext4_crypt.cpp @@ -2,9 +2,7 @@ * Copyright (c) 2015 Google, Inc. */ -#define TAG "ext4_utils" - -#include "ext4_crypt_init_extensions.h" +#include "ext4_crypt.h" #include #include @@ -17,7 +15,7 @@ #include #include -#include +#include #define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy" #define EXT4_KEYREF_DELIMITER ((char)'.') @@ -25,6 +23,8 @@ // ext4enc:TODO Include structure from somewhere sensible // MUST be in sync with ext4_crypto.c in kernel #define EXT4_KEY_DESCRIPTOR_SIZE 8 +#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17 + struct ext4_encryption_policy { char version; char contents_encryption_mode; @@ -37,18 +37,17 @@ struct ext4_encryption_policy { #define EXT4_ENCRYPTION_MODE_AES_256_CTS 4 // ext4enc:TODO Get value from somewhere sensible -#define EXT4_IOC_SET_ENCRYPTION_POLICY \ - _IOR('f', 19, struct ext4_encryption_policy) +#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy) +#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy) -/* Validate that all path items are available and accessible. */ -static int is_path_valid(const char *path) -{ - if (access(path, W_OK)) { - KLOG_ERROR(TAG, "Can't access %s: %s\n",strerror(errno), path); - return 0; - } +#define HEX_LOOKUP "0123456789abcdef" - return 1; +static void policy_to_hex(const char* policy, char* hex) { + for (size_t i = 0, j = 0; i < EXT4_KEY_DESCRIPTOR_SIZE; i++) { + hex[j++] = HEX_LOOKUP[(policy[i] & 0xF0) >> 4]; + hex[j++] = HEX_LOOKUP[policy[i] & 0x0F]; + } + hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX - 1] = '\0'; } static int is_dir_empty(const char *dirname) @@ -69,36 +68,26 @@ static int is_dir_empty(const char *dirname) return n <= 2; } -int do_policy_set(const char *directory, const char *policy, int policy_length) -{ - struct stat st; - ssize_t ret; - +int e4crypt_policy_set(const char *directory, const char *policy, size_t policy_length) { if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) { - KLOG_ERROR("Policy wrong length\n"); - return -EINVAL; + LOG(ERROR) << "Policy wrong length: " << policy_length; + return -1; } - if (!is_path_valid(directory)) { - return -EINVAL; + if (access(directory, W_OK)) { + PLOG(ERROR) << "Failed to access directory " << directory; + return -1; } - stat(directory, &st); - if (!S_ISDIR(st.st_mode)) { - KLOG_ERROR(TAG, "Can only set policy on a directory (%s)\n", directory); - return -EINVAL; + int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (fd == -1) { + PLOG(ERROR) << "Failed to open directory " << directory; + return -1; } if (!is_dir_empty(directory)) { - KLOG_ERROR(TAG, "Can only set policy on an empty directory (%s)\n", - directory); - return -EINVAL; - } - - int fd = open(directory, O_DIRECTORY); - if (fd == -1) { - KLOG_ERROR(TAG, "Failed to open directory (%s)\n", directory); - return -EINVAL; + LOG(ERROR) << "Can only set policy on an empty directory " << directory; + return -1; } ext4_encryption_policy eep; @@ -107,17 +96,84 @@ int do_policy_set(const char *directory, const char *policy, int policy_length) eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS; eep.flags = 0; memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE); - ret = ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep); - auto preserve_errno = errno; - close(fd); - - if (ret) { - KLOG_ERROR(TAG, "Failed to set encryption policy for %s: %s\n", - directory, strerror(preserve_errno)); - return -EINVAL; + if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) { + PLOG(ERROR) << "Failed to set encryption policy for " << directory; + close(fd); + return -1; + } else { + close(fd); } - KLOG_INFO(TAG, "Encryption policy for %s is set to %02x%02x%02x%02x\n", - directory, policy[0], policy[1], policy[2], policy[3]); + char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(policy, policy_hex); + LOG(INFO) << "Policy for " << directory << " set to " << policy_hex; return 0; } + +int e4crypt_policy_get(const char *directory, char *policy, size_t policy_length) { + if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) { + LOG(ERROR) << "Policy wrong length: " << policy_length; + return -1; + } + + if (access(directory, W_OK)) { + PLOG(ERROR) << "Failed to access directory " << directory; + return -1; + } + + int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (fd == -1) { + PLOG(ERROR) << "Failed to open directory " << directory; + return -1; + } + + ext4_encryption_policy eep; + memset(&eep, 0, sizeof(ext4_encryption_policy)); + + if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &eep)) { + PLOG(WARNING) << "Failed to get encryption policy for " << directory; + close(fd); + return -1; + } else { + close(fd); + } + + if ((eep.version == 0) + && (eep.contents_encryption_mode == EXT4_ENCRYPTION_MODE_AES_256_XTS) + && (eep.filenames_encryption_mode == EXT4_ENCRYPTION_MODE_AES_256_CTS) + && (eep.flags == 0)) { + memcpy(policy, eep.master_key_descriptor, EXT4_KEY_DESCRIPTOR_SIZE); + return 0; + } + + LOG(ERROR) << "Failed to find matching encryption policy for " << directory; + return -1; +} + +int e4crypt_policy_ensure(const char *directory, const char *policy, size_t policy_length) { + if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) { + LOG(ERROR) << "Policy wrong length: " << policy_length; + return -1; + } + + char existing_policy[EXT4_KEY_DESCRIPTOR_SIZE]; + if (e4crypt_policy_get(directory, existing_policy, EXT4_KEY_DESCRIPTOR_SIZE) == 0) { + char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX]; + char existing_policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX]; + + policy_to_hex(policy, policy_hex); + policy_to_hex(existing_policy, existing_policy_hex); + + if (memcmp(policy, existing_policy, EXT4_KEY_DESCRIPTOR_SIZE) == 0) { + LOG(INFO) << "Found policy " << existing_policy_hex << " at " << directory + << " which matches expected value"; + return 0; + } else { + LOG(ERROR) << "Found policy " << existing_policy_hex << " at " << directory + << " which doesn't match expected value " << policy_hex; + return -1; + } + } + + return e4crypt_policy_set(directory, policy, policy_length); +} diff --git a/ext4_utils/ext4_crypt.h b/ext4_utils/ext4_crypt.h new file mode 100644 index 00000000..4eb74e29 --- /dev/null +++ b/ext4_utils/ext4_crypt.h @@ -0,0 +1,30 @@ +/* + * 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 +#include +#include + +__BEGIN_DECLS + +int e4crypt_policy_get(const char *directory, char* policy, size_t policy_length); +int e4crypt_policy_set(const char *directory, const char* policy, size_t policy_length); +int e4crypt_policy_ensure(const char *directory, const char* policy, size_t policy_length); + +static const char* e4crypt_unencrypted_folder = "/unencrypted"; +static const char* e4crypt_key_ref = "/unencrypted/ref"; + +__END_DECLS diff --git a/ext4_utils/ext4_crypt_init_extensions.cpp b/ext4_utils/ext4_crypt_init_extensions.cpp index 81ddf956..56c3b097 100644 --- a/ext4_utils/ext4_crypt_init_extensions.cpp +++ b/ext4_utils/ext4_crypt_init_extensions.cpp @@ -1,6 +1,25 @@ +/* + * 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. + */ + #define TAG "ext4_utils" #include "ext4_crypt_init_extensions.h" +#include "ext4_crypt.h" + +#include #include #include @@ -16,86 +35,33 @@ #include #include #include -#include +#include #include "key_control.h" static const std::string arbitrary_sequence_number = "42"; static const int vold_command_timeout_ms = 60 * 1000; -static std::string vold_command(std::string const& command) -{ - KLOG_INFO(TAG, "Running command %s\n", command.c_str()); - int sock = -1; - - while (true) { - sock = socket_local_client("cryptd", - ANDROID_SOCKET_NAMESPACE_RESERVED, - SOCK_STREAM); - if (sock >= 0) { - break; - } - usleep(10000); - } - - if (sock < 0) { - KLOG_INFO(TAG, "Cannot open vold, failing command (%s)\n", strerror(errno)); - return ""; - } - - class CloseSocket - { - int sock_; - public: - CloseSocket(int sock) : sock_(sock) {} - ~CloseSocket() { close(sock_); } - }; - - CloseSocket cs(sock); - - // Use arbitrary sequence number. This should only be used when the - // framework is down, so this is (mostly) OK. - std::string actual_command = arbitrary_sequence_number + " " + command; - if (write(sock, actual_command.c_str(), actual_command.size() + 1) < 0) { - KLOG_ERROR(TAG, "Cannot write command (%s)\n", strerror(errno)); - return ""; - } - - struct pollfd poll_sock = {sock, POLLIN, 0}; - - int rc = TEMP_FAILURE_RETRY(poll(&poll_sock, 1, vold_command_timeout_ms)); - if (rc < 0) { - KLOG_ERROR(TAG, "Error in poll (%s)\n", strerror(errno)); - return ""; - } - - if (!(poll_sock.revents & POLLIN)) { - KLOG_ERROR(TAG, "Timeout\n"); - return ""; - } - char buffer[4096]; - memset(buffer, 0, sizeof(buffer)); - rc = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer))); - if (rc <= 0) { - if (rc == 0) { - KLOG_ERROR(TAG, "Lost connection to Vold - did it crash?\n"); - } else { - KLOG_ERROR(TAG, "Error reading data (%s)\n", strerror(errno)); - } - return ""; +static void kernel_logger(android::base::LogId, android::base::LogSeverity severity, const char*, + const char*, unsigned int, const char* message) { + if (severity == android::base::ERROR || severity == android::base::FATAL) { + KLOG_ERROR(TAG, "%s\n", message); + } else if (severity == android::base::WARNING) { + KLOG_WARNING(TAG, "%s\n", message); + } else { + KLOG_INFO(TAG, "%s\n", message); } +} - // We don't truly know that this is the correct result. However, - // since this will only be used when the framework is down, - // it should be OK unless someone is running vdc at the same time. - // Worst case we force a reboot in the very rare synchronization - // error - return std::string(buffer, rc); +static void init_logging() { + android::base::SetLogger(kernel_logger); } int e4crypt_create_device_key(const char* dir, int ensure_dir_exists(const char*)) { + init_logging(); + // Make sure folder exists. Use make_dir to set selinux permissions. std::string unencrypted_dir = std::string(dir) + "/unencrypted"; if (ensure_dir_exists(unencrypted_dir.c_str())) { @@ -105,16 +71,16 @@ int e4crypt_create_device_key(const char* dir, return -1; } - auto result = vold_command("cryptfs enablefilecrypto"); - // ext4enc:TODO proper error handling - KLOG_INFO(TAG, "enablefilecrypto returned with result %s\n", - result.c_str()); - - return 0; + const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "enablefilecrypto" }; + int rc = android_fork_execvp(4, (char**) argv, NULL, false, true); + LOG(INFO) << "enablefilecrypto result: " << rc; + return rc; } int e4crypt_install_keyring() { + init_logging(); + key_serial_t device_keyring = add_key("keyring", "e4crypt", 0, 0, KEY_SPEC_SESSION_KEYRING); @@ -123,7 +89,7 @@ int e4crypt_install_keyring() return -1; } - KLOG_INFO(TAG, "Keyring created wth id %d in process %d\n", + KLOG_INFO(TAG, "Keyring created with id %d in process %d\n", device_keyring, getpid()); return 0; @@ -131,15 +97,18 @@ int e4crypt_install_keyring() int e4crypt_do_init_user0() { - auto result = vold_command("cryptfs init_user0"); - // ext4enc:TODO proper error handling - KLOG_INFO(TAG, "init_user0 returned with result %s\n", - result.c_str()); - return 0; + init_logging(); + + const char* argv[] = { "/system/bin/vdc", "--wait", "cryptfs", "init_user0" }; + int rc = android_fork_execvp(4, (char**) argv, NULL, false, true); + LOG(INFO) << "init_user0 result: " << rc; + return rc; } int e4crypt_set_directory_policy(const char* dir) { + init_logging(); + // 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 @@ -152,7 +121,11 @@ int e4crypt_set_directory_policy(const char* dir) // often because their subdirectories must be encrypted. // This isn't a nice way to do this, see b/26641735 std::vector directories_to_exclude = { - "lost+found", "user", "system_ce", "media", "data", "user_de", + "lost+found", + "system_ce", "system_de", + "misc_ce", "misc_de", + "media", + "data", "user", "user_de", // ext4enc:TODO workaround that must be removed b/26673855 "misc", }; @@ -172,7 +145,7 @@ int e4crypt_set_directory_policy(const char* dir) } KLOG_INFO(TAG, "Setting policy on %s\n", dir); - int result = do_policy_set(dir, policy.c_str(), policy.size()); + int result = e4crypt_policy_ensure(dir, policy.c_str(), policy.size()); if (result) { KLOG_ERROR(TAG, "Setting %02x%02x%02x%02x policy on %s failed!\n", policy[0], policy[1], policy[2], policy[3], dir); diff --git a/ext4_utils/ext4_crypt_init_extensions.h b/ext4_utils/ext4_crypt_init_extensions.h index f5643561..63f6d88c 100644 --- a/ext4_utils/ext4_crypt_init_extensions.h +++ b/ext4_utils/ext4_crypt_init_extensions.h @@ -1,3 +1,19 @@ +/* + * 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 #include #include @@ -11,9 +27,5 @@ int e4crypt_create_device_key(const char* path, int ensure_dir_exists(const char* dir)); int e4crypt_set_directory_policy(const char* path); int e4crypt_do_init_user0(); -int do_policy_set(const char *directory, const char *policy, int policy_length); - -static const char* e4crypt_unencrypted_folder = "/unencrypted"; -static const char* e4crypt_key_ref = "/unencrypted/ref"; __END_DECLS -- 2.11.0