OSDN Git Service

Add methods to get and ensure encryption policy.
authorJeff Sharkey <jsharkey@android.com>
Wed, 3 Feb 2016 22:30:33 +0000 (15:30 -0700)
committerJeff Sharkey <jsharkey@android.com>
Fri, 5 Feb 2016 17:58:47 +0000 (10:58 -0700)
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
ext4_utils/ext4_crypt.cpp
ext4_utils/ext4_crypt.h [new file with mode: 0644]
ext4_utils/ext4_crypt_init_extensions.cpp
ext4_utils/ext4_crypt_init_extensions.h

index 3bfab5c..babf5ca 100644 (file)
@@ -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
index 4000447..37e17c5 100644 (file)
@@ -2,9 +2,7 @@
  * Copyright (c) 2015 Google, Inc.
  */
 
-#define TAG "ext4_utils"
-
-#include "ext4_crypt_init_extensions.h"
+#include "ext4_crypt.h"
 
 #include <dirent.h>
 #include <errno.h>
@@ -17,7 +15,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <cutils/klog.h>
+#include <android-base/logging.h>
 
 #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 (file)
index 0000000..4eb74e2
--- /dev/null
@@ -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 <sys/cdefs.h>
+#include <stdbool.h>
+#include <cutils/multiuser.h>
+
+__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
index 81ddf95..56c3b09 100644 (file)
@@ -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 <android-base/logging.h>
 
 #include <string>
 #include <vector>
 #include <cutils/klog.h>
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
-#include <poll.h>
+#include <logwrap/logwrap.h>
 
 #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<std::string> 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);
index f564356..63f6d88 100644 (file)
@@ -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 <sys/cdefs.h>
 #include <stdbool.h>
 #include <cutils/multiuser.h>
@@ -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