summaryrefslogtreecommitdiff
path: root/libfscrypt/fscrypt.cpp
diff options
context:
space:
mode:
authorEric Biggers <ebiggers@google.com>2018-10-23 13:39:07 -0700
committerEric Biggers <ebiggers@google.com>2018-10-25 17:10:15 -0700
commitb46c3591e1e2b091d0fb727715a8d617dfc80aa6 (patch)
tree8336261d742bfe2a0dbe7467cf3093efec4f38ee /libfscrypt/fscrypt.cpp
parent67f1b299ade36c4b419d15fd1d63e1dfb5b222f2 (diff)
downloadextras-b46c3591e1e2b091d0fb727715a8d617dfc80aa6.tar.gz
Refactor "ext4 encryption" code into libfscrypt
File-based encryption related code no longer belongs in ext4_utils, as it is now used on both ext4 and f2fs, since both filesystems share the same kernel API for encryption. Refactor it into its own library, libfscrypt. Note that the keyring is renamed from "e4crypt" to "fscrypt", which is technically a user-visible change, but as far as I know nothing depends on it other than vold which is being updated too. Test: built, booted device with f2fs encryption Change-Id: I3c302564262412a5d5e672bd213e7cfada5f49cc
Diffstat (limited to 'libfscrypt/fscrypt.cpp')
-rw-r--r--libfscrypt/fscrypt.cpp285
1 files changed, 285 insertions, 0 deletions
diff --git a/libfscrypt/fscrypt.cpp b/libfscrypt/fscrypt.cpp
new file mode 100644
index 00000000..59297318
--- /dev/null
+++ b/libfscrypt/fscrypt.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2015 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 <array>
+
+#include <asm/ioctl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+#include <utils/misc.h>
+
+// TODO Include these definitions from <linux/fs.h>
+
+#define FS_KEY_DESCRIPTOR_SIZE 8
+#define FS_KEY_DESCRIPTOR_SIZE_HEX 17
+
+struct fscrypt_policy {
+ uint8_t version;
+ uint8_t contents_encryption_mode;
+ uint8_t filenames_encryption_mode;
+ uint8_t flags;
+ uint8_t master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
+#define FS_ENCRYPTION_MODE_AES_256_XTS 1
+#define FS_ENCRYPTION_MODE_AES_256_CTS 4
+#define FS_ENCRYPTION_MODE_AES_256_HEH 126
+#define FS_ENCRYPTION_MODE_PRIVATE 127
+
+#define FS_POLICY_FLAGS_PAD_4 0x00
+#define FS_POLICY_FLAGS_PAD_8 0x01
+#define FS_POLICY_FLAGS_PAD_16 0x02
+#define FS_POLICY_FLAGS_PAD_32 0x03
+#define FS_POLICY_FLAGS_PAD_MASK 0x03
+#define FS_POLICY_FLAGS_VALID 0x03
+
+#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
+#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
+
+#define HEX_LOOKUP "0123456789abcdef"
+
+bool fscrypt_is_native() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.crypto.type", value, "none");
+ return !strcmp(value, "file");
+}
+
+static void log_ls(const char* dirname) {
+ std::array<const char*, 3> argv = {"ls", "-laZ", dirname};
+ int status = 0;
+ auto res =
+ 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;
+ }
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << argv[0] << " " << argv[1] << " " << argv[2]
+ << " did not exit normally, status: " << status;
+ return;
+ }
+ if (WEXITSTATUS(status) != 0) {
+ LOG(ERROR) << argv[0] << " " << argv[1] << " " << argv[2]
+ << " returned failure: " << WEXITSTATUS(status);
+ return;
+ }
+}
+
+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 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;
+}
+
+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;
+ }
+ // 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;
+ }
+ 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;
+ }
+
+ 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;
+}
+
+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;
+ }
+
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+
+ 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;
+ }
+ 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;
+ }
+ memcpy(policy, fp.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE);
+
+ return true;
+}
+
+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;
+ }
+ 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;
+}
+
+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, "ice")) {
+ contents_mode = FS_ENCRYPTION_MODE_PRIVATE;
+ } else {
+ LOG(ERROR) << "Invalid file contents encryption mode: "
+ << contents_encryption_mode;
+ return -1;
+ }
+
+ 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 {
+ LOG(ERROR) << "Invalid file names encryption mode: "
+ << filenames_encryption_mode;
+ return -1;
+ }
+
+ 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 {
+ if (!fscrypt_policy_check(directory, policy, policy_length,
+ contents_mode, filenames_mode)) return -1;
+ }
+ return 0;
+}