From 79f88bdf8d54a84b7bb727b0c28b2dfcdc3d14d5 Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Fri, 9 Dec 2016 14:52:12 -0600 Subject: Support backup/restore of FBE policies Change-Id: Iba8ef20f57b0fb57bb9406c53148a806441d0b59 --- crypto/ext4crypt/Android.mk | 13 ++- crypto/ext4crypt/Decrypt.cpp | 62 ++++++++++++++ crypto/ext4crypt/Ext4Crypt.cpp | 12 ++- crypto/ext4crypt/Ext4Crypt.h | 4 + crypto/ext4crypt/e4policyget.cpp | 44 ++++++++++ crypto/ext4crypt/ext4_crypt.cpp | 180 +++++++++++++++++++++++++++++++++++++++ crypto/ext4crypt/ext4crypt_tar.h | 38 +++++++++ libtar/Android.mk | 34 +++++--- libtar/append.c | 32 +++++++ libtar/block.c | 113 ++++++++++++++++++++++++ libtar/extract.c | 28 +++++- libtar/libtar.h | 11 +++ partition.cpp | 25 +++--- twrpTar.cpp | 34 +++++--- 14 files changed, 585 insertions(+), 45 deletions(-) create mode 100644 crypto/ext4crypt/e4policyget.cpp create mode 100644 crypto/ext4crypt/ext4_crypt.cpp create mode 100644 crypto/ext4crypt/ext4crypt_tar.h diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk index 531aca199..bcbcccfa2 100644 --- a/crypto/ext4crypt/Android.mk +++ b/crypto/ext4crypt/Android.mk @@ -5,7 +5,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := libe4crypt LOCAL_MODULE_TAGS := eng optional LOCAL_CFLAGS := -LOCAL_SRC_FILES := Decrypt.cpp Ext4Crypt.cpp Keymaster.cpp KeyStorage.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp +LOCAL_SRC_FILES := Decrypt.cpp Ext4Crypt.cpp Keymaster.cpp KeyStorage.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp ext4_crypt.cpp LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libsoftkeymaster libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite LOCAL_STATIC_LIBRARIES := libscrypt_static LOCAL_C_INCLUDES := system/extras/ext4_utils external/scrypt/lib/crypto system/security/keystore hardware/libhardware/include/hardware system/security/softkeymaster/include/keymaster system/keymaster/include @@ -30,4 +30,15 @@ LOCAL_SHARED_LIBRARIES := libe4crypt include $(BUILD_EXECUTABLE) +include $(CLEAR_VARS) +LOCAL_MODULE := e4policyget +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := e4policyget.cpp +LOCAL_SHARED_LIBRARIES := libe4crypt +LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + +include $(BUILD_EXECUTABLE) + endif diff --git a/crypto/ext4crypt/Decrypt.cpp b/crypto/ext4crypt/Decrypt.cpp index 6c47add0c..3b69d4651 100644 --- a/crypto/ext4crypt/Decrypt.cpp +++ b/crypto/ext4crypt/Decrypt.cpp @@ -17,10 +17,12 @@ #include "Decrypt.h" #include "Ext4Crypt.h" +#include #include #include #include +#include #include #include @@ -32,6 +34,66 @@ #include +// Store main DE raw ref / policy +extern std::string de_raw_ref; +extern std::map s_de_key_raw_refs; +extern std::map s_ce_key_raw_refs; + +static bool lookup_ref_key_internal(std::map& key_map, const char* policy, userid_t* user_id) { + for (std::map::iterator it=key_map.begin(); it!=key_map.end(); ++it) { + if (strncmp(it->second.c_str(), policy, it->second.size()) == 0) { + *user_id = it->first; + return true; + } + } + return false; +} + +extern "C" bool lookup_ref_key(const char* policy, char* policy_type) { + userid_t user_id = 0; + if (strncmp(de_raw_ref.c_str(), policy, de_raw_ref.size()) == 0) { + strcpy(policy_type, "1DK"); + return true; + } + if (!lookup_ref_key_internal(s_de_key_raw_refs, policy, &user_id)) { + if (!lookup_ref_key_internal(s_ce_key_raw_refs, policy, &user_id)) { + return false; + } else + sprintf(policy_type, "1CE%d", user_id); + } else + sprintf(policy_type, "1DE%d", user_id); + return true; +} + +extern "C" bool lookup_ref_tar(const char* policy_type, char* policy) { + if (strncmp(policy_type, "1", 1) != 0) { + printf("Unexpected version %c\n", policy_type); + return false; + } + const char* ptr = policy_type + 1; // skip past the version number + if (strncmp(ptr, "DK", 2) == 0) { + strncpy(policy, de_raw_ref.data(), de_raw_ref.size()); + return true; + } + userid_t user_id = atoi(ptr + 2); + std::string raw_ref; + if (*ptr == 'D') { + if (lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref)) { + strncpy(policy, raw_ref.data(), raw_ref.size()); + } else + return false; + } else if (*ptr == 'C') { + if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) { + strncpy(policy, raw_ref.data(), raw_ref.size()); + } else + return false; + } else { + printf("unknown policy type '%s'\n", policy_type); + return false; + } + return true; +} + int gatekeeper_device_initialize(gatekeeper_device_t **dev) { int ret; const hw_module_t *mod; diff --git a/crypto/ext4crypt/Ext4Crypt.cpp b/crypto/ext4crypt/Ext4Crypt.cpp index 423147d66..8bc419992 100644 --- a/crypto/ext4crypt/Ext4Crypt.cpp +++ b/crypto/ext4crypt/Ext4Crypt.cpp @@ -67,6 +67,12 @@ using android::vold::kEmptyAuthentication; //static constexpr int FLAG_STORAGE_DE = 1 << 0; // moved to Decrypt.h //static constexpr int FLAG_STORAGE_CE = 1 << 1; +// Store main DE raw ref / policy +std::string de_raw_ref; +// Map user ids to key references +std::map s_de_key_raw_refs; +std::map s_ce_key_raw_refs; + namespace { const std::string device_key_dir = std::string() + DATA_MNT_POINT + e4crypt_unencrypted_folder; const std::string device_key_path = device_key_dir + "/key"; @@ -80,9 +86,6 @@ bool s_global_de_initialized = false; // Some users are ephemeral, don't try to wipe their keys from disk std::set s_ephemeral_users; -// Map user ids to key references -std::map s_de_key_raw_refs; -std::map s_ce_key_raw_refs; // TODO abolish this map. Keys should not be long-lived in user memory, only kernel memory. // See b/26948053 std::map s_ce_keys; @@ -290,7 +293,7 @@ static bool path_exists(const std::string& path) { return access(path.c_str(), F_OK) == 0; } -static bool lookup_key_ref(const std::map& key_map, userid_t user_id, +bool lookup_key_ref(const std::map& key_map, userid_t user_id, std::string* raw_ref) { auto refi = key_map.find(user_id); if (refi == key_map.end()) { @@ -379,6 +382,7 @@ bool e4crypt_initialize_global_de() { } s_global_de_initialized = true; + de_raw_ref = device_key_ref; return true; } diff --git a/crypto/ext4crypt/Ext4Crypt.h b/crypto/ext4crypt/Ext4Crypt.h index b05ff4ddb..57623e35c 100644 --- a/crypto/ext4crypt/Ext4Crypt.h +++ b/crypto/ext4crypt/Ext4Crypt.h @@ -19,6 +19,7 @@ #include +#include #include __BEGIN_DECLS @@ -40,4 +41,7 @@ bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token, co bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial, int flags); //bool e4crypt_destroy_user_storage(const char* volume_uuid, userid_t user_id, int flags); +bool lookup_key_ref(const std::map& key_map, userid_t user_id, + std::string* raw_ref); + __END_DECLS diff --git a/crypto/ext4crypt/e4policyget.cpp b/crypto/ext4crypt/e4policyget.cpp new file mode 100644 index 000000000..d217f18ee --- /dev/null +++ b/crypto/ext4crypt/e4policyget.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 Team Win Recovery 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 +#include "ext4crypt_tar.h" + +#define EXT4_KEY_DESCRIPTOR_SIZE 8 +#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17 + +int main(int argc, char *argv[]) { + bool ret = false; + if (argc != 2) { + printf("Must specify a path\n"); + return -1; + } else { + char e4crypt_policy[EXT4_KEY_DESCRIPTOR_SIZE]; + if (e4crypt_policy_get(argv[1], e4crypt_policy, EXT4_KEY_DESCRIPTOR_SIZE, 0)) + { + char* ptr = tar_policy; + memset(tar_policy, 0, sizeof(tar_policy)); + char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(e4crypt_policy, policy_hex); + printf("%s\n", policy_hex); + } else { + printf("No policy set\n"); + } + } + return 0; +} diff --git a/crypto/ext4crypt/ext4_crypt.cpp b/crypto/ext4crypt/ext4_crypt.cpp new file mode 100644 index 000000000..029db7567 --- /dev/null +++ b/crypto/ext4crypt/ext4_crypt.cpp @@ -0,0 +1,180 @@ +/* + * 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. + */ + +/* TWRP NOTE: Kanged from system/extras/ext4_utils/ext4_crypt.cpp + * because policy_to_hex, e4crypt_policy_set, and e4crypt_policy_get + * are not exposed to be used. There was also a bug in e4crypt_policy_get + * that may or may not be fixed in the user's local repo: + * https://android.googlesource.com/platform/system/extras/+/30b93dd5715abcabd621235733733c0503f9c552 + */ + +#include "ext4_crypt.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy" +#define EXT4_KEYREF_DELIMITER ((char)'.') + +// 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; + char filenames_encryption_mode; + char flags; + char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE]; +} __attribute__((__packed__)); + +#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1 +#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4 +#define EXT4_ENCRYPTION_MODE_PRIVATE 127 + +static int encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE; + +// ext4enc:TODO Get value from somewhere sensible +#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) + +#define HEX_LOOKUP "0123456789abcdef" + +extern "C" 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'; +} + +extern "C" bool e4crypt_policy_set(const char *directory, const char *policy, + size_t policy_length, int contents_encryption_mode) { + if (contents_encryption_mode == 0) + contents_encryption_mode = encryption_mode; + if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) { + printf("policy wrong length\n"); + LOG(ERROR) << "Policy wrong length: " << policy_length; + return false; + } + int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (fd == -1) { + printf("failed to open %s\n", directory); + PLOG(ERROR) << "Failed to open directory " << directory; + return false; + } + + ext4_encryption_policy eep; + eep.version = 0; + eep.contents_encryption_mode = contents_encryption_mode; + eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS; + eep.flags = 0; + memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE); + if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) { + printf("failed to set policy for '%s' '%s'\n", directory, policy); + PLOG(ERROR) << "Failed to set encryption policy for " << directory; + close(fd); + return false; + } + close(fd); + + char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(policy, policy_hex); + LOG(INFO) << "Policy for " << directory << " set to " << policy_hex; + return true; +} + +extern "C" bool e4crypt_policy_get(const char *directory, char *policy, + size_t policy_length, int contents_encryption_mode) { + if (contents_encryption_mode == 0) + contents_encryption_mode = encryption_mode; + if (policy_length != EXT4_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; + } + + ext4_encryption_policy eep; + memset(&eep, 0, sizeof(ext4_encryption_policy)); + if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &eep) != 0) { + PLOG(ERROR) << "Failed to get encryption policy for " << directory; + close(fd); + return false; + } + close(fd); + + if ((eep.version != 0) + || (eep.contents_encryption_mode != contents_encryption_mode) + || (eep.filenames_encryption_mode != EXT4_ENCRYPTION_MODE_AES_256_CTS) + || (eep.flags != 0)) { + LOG(ERROR) << "Failed to find matching encryption policy for " << directory; + return false; + } + memcpy(policy, eep.master_key_descriptor, EXT4_KEY_DESCRIPTOR_SIZE); + + return true; +} + +extern "C" bool e4crypt_set_mode() { + const char* mode_file = "/data/unencrypted/mode"; + struct stat st; + if (stat(mode_file, &st) != 0 || st.st_size <= 0) { + printf("Invalid encryption mode file %s\n", mode_file); + return false; + } + size_t mode_size = st.st_size; + char contents_encryption_mode[mode_size + 1]; + memset((void*)contents_encryption_mode, 0, mode_size + 1); + int fd = open(mode_file, O_RDONLY); + if (fd < 0) { + printf("error opening '%s': %s\n", mode_file, strerror(errno)); + return false; + } + if (read(fd, contents_encryption_mode, mode_size) != mode_size) { + printf("read error on '%s': %s\n", mode_file, strerror(errno)); + close(fd); + return false; + } + close(fd); + if (!strcmp(contents_encryption_mode, "software")) { + encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS; + } else if (!strcmp(contents_encryption_mode, "ice")) { + encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE; + } else { + printf("Invalid encryption mode '%s'\n", contents_encryption_mode); + return false; + } + printf("set encryption mode to %i\n", encryption_mode); + return true; +} diff --git a/crypto/ext4crypt/ext4crypt_tar.h b/crypto/ext4crypt/ext4crypt_tar.h new file mode 100644 index 000000000..1c9cef0a5 --- /dev/null +++ b/crypto/ext4crypt/ext4crypt_tar.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 Team Win Recovery 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 __EXT4CRYPT_TAR_H +#define __EXT4CRYPT_TAR_H + +#include +#include +#include + +__BEGIN_DECLS + +bool lookup_ref_key(const char* policy, char* policy_type); +bool lookup_ref_tar(const char* policy_type, char* policy); + +void policy_to_hex(const char* policy, char* hex); +bool e4crypt_policy_set(const char *directory, const char *policy, + size_t policy_length, int contents_encryption_mode); +bool e4crypt_policy_get(const char *directory, char *policy, + size_t policy_length, int contents_encryption_mode); + +bool e4crypt_set_mode(); +__END_DECLS + +#endif diff --git a/libtar/Android.mk b/libtar/Android.mk index 838b44175..14c19f73f 100644 --- a/libtar/Android.mk +++ b/libtar/Android.mk @@ -5,16 +5,21 @@ include $(CLEAR_VARS) LOCAL_MODULE := libtar LOCAL_MODULE_TAGS := eng optional -LOCAL_CFLAGS := -LOCAL_SRC_FILES = append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c +LOCAL_SRC_FILES := append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c LOCAL_C_INCLUDES += $(LOCAL_PATH) \ - external/zlib + external/zlib LOCAL_SHARED_LIBRARIES += libz libc ifeq ($(TWHAVE_SELINUX), true) - LOCAL_C_INCLUDES += external/libselinux/include - LOCAL_SHARED_LIBRARIES += libselinux - LOCAL_CFLAGS += -DHAVE_SELINUX + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_SHARED_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX +endif + +ifeq ($(TW_INCLUDE_CRYPTO_FBE), true) + LOCAL_SHARED_LIBRARIES += libe4crypt + LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT + LOCAL_C_INCLUDES += bootable/recovery/crypto/ext4crypt endif include $(BUILD_SHARED_LIBRARY) @@ -24,16 +29,21 @@ include $(CLEAR_VARS) LOCAL_MODULE := libtar_static LOCAL_MODULE_TAGS := eng optional -LOCAL_CFLAGS = -LOCAL_SRC_FILES = append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c +LOCAL_SRC_FILES := append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c LOCAL_C_INCLUDES += $(LOCAL_PATH) \ - external/zlib + external/zlib LOCAL_STATIC_LIBRARIES += libz libc ifeq ($(TWHAVE_SELINUX), true) - LOCAL_C_INCLUDES += external/libselinux/include - LOCAL_STATIC_LIBRARIES += libselinux - LOCAL_CFLAGS += -DHAVE_SELINUX + LOCAL_C_INCLUDES += external/libselinux/include + LOCAL_STATIC_LIBRARIES += libselinux + LOCAL_CFLAGS += -DHAVE_SELINUX +endif + +ifeq ($(TW_INCLUDE_CRYPTO_FBE), true) + LOCAL_SHARED_LIBRARIES += libe4crypt + LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT + LOCAL_C_INCLUDES += bootable/recovery/crypto/ext4crypt endif include $(BUILD_STATIC_LIBRARY) diff --git a/libtar/append.c b/libtar/append.c index 4be679ccd..438829753 100644 --- a/libtar/append.c +++ b/libtar/append.c @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef STDC_HEADERS # include @@ -34,6 +35,10 @@ # include "selinux/selinux.h" #endif +#ifdef HAVE_EXT4_CRYPT +# include "ext4crypt_tar.h" +#endif + struct tar_dev { dev_t td_dev; @@ -122,6 +127,33 @@ tar_append_file(TAR *t, const char *realname, const char *savename) } #endif +#ifdef HAVE_EXT4_CRYPT + if (TH_ISDIR(t) && t->options & TAR_STORE_EXT4_POL) + { + if (t->th_buf.e4crypt_policy != NULL) + { + free(t->th_buf.e4crypt_policy); + t->th_buf.e4crypt_policy = NULL; + } + + char e4crypt_policy[EXT4_KEY_DESCRIPTOR_SIZE]; + if (e4crypt_policy_get(realname, e4crypt_policy, EXT4_KEY_DESCRIPTOR_SIZE, 0)) + { + char tar_policy[EXT4_KEY_DESCRIPTOR_SIZE]; + memset(tar_policy, 0, sizeof(tar_policy)); + char policy_hex[EXT4_KEY_DESCRIPTOR_HEX]; + policy_to_hex(e4crypt_policy, policy_hex); + if (lookup_ref_key(e4crypt_policy, &tar_policy)) { + printf("found policy '%s' - '%s' - '%s'\n", realname, tar_policy, policy_hex); + t->th_buf.e4crypt_policy = strdup(tar_policy); + } else { + printf("failed to lookup tar policy for '%s' - '%s'\n", realname, policy_hex); + return -1; + } + } // else no policy found, but this is not an error as not all dirs will have a policy + } +#endif + /* check if it's a hardlink */ #ifdef DEBUG puts("tar_append_file(): checking inode cache for hardlink..."); diff --git a/libtar/block.c b/libtar/block.c index 5d3c9d826..2fd61bb76 100644 --- a/libtar/block.c +++ b/libtar/block.c @@ -25,6 +25,10 @@ #define SELINUX_TAG "RHT.security.selinux=" #define SELINUX_TAG_LEN 21 +// Used to identify e4crypt_policy in extended ('x') +#define E4CRYPT_TAG "TWRP.security.e4crypt=" +#define E4CRYPT_TAG_LEN 22 + /* read a header block */ /* FIXME: the return value of this function should match the return value of tar_block_read(), which is a macro which references a prototype @@ -119,6 +123,11 @@ th_read(TAR *t) if (t->th_buf.selinux_context != NULL) free(t->th_buf.selinux_context); #endif +#ifdef HAVE_EXT4_CRYPT + if (t->th_buf.e4crypt_policy != NULL) { + free(t->th_buf.e4crypt_policy); + } +#endif memset(&(t->th_buf), 0, sizeof(struct tar_header)); @@ -283,6 +292,57 @@ th_read(TAR *t) } #endif +#ifdef HAVE_EXT4_CRYPT + if(TH_ISPOLHEADER(t)) + { + sz = th_get_size(t); + + if(sz >= T_BLOCKSIZE) // Not supported + { +#ifdef DEBUG + printf(" th_read(): Policy header is too long!\n"); +#endif + } + else + { + char buf[T_BLOCKSIZE]; + i = tar_block_read(t, buf); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + + // To be sure + buf[T_BLOCKSIZE-1] = 0; + + int len = strlen(buf); + char *start = strstr(buf, E4CRYPT_TAG); + if(start && start+E4CRYPT_TAG_LEN < buf+len) + { + start += E4CRYPT_TAG_LEN; + char *end = strchr(start, '\n'); + if(end) + { + t->th_buf.e4crypt_policy = strndup(start, end-start); +#ifdef DEBUG + printf(" th_read(): E4Crypt policy detected: %s\n", t->th_buf.e4crypt_policy); +#endif + } + } + } + + i = th_read_internal(t); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + } +#endif + return 0; } @@ -457,6 +517,59 @@ th_write(TAR *t) } #endif +#ifdef HAVE_EXT4_CRYPT + if((t->options & TAR_STORE_EXT4_POL) && t->th_buf.e4crypt_policy != NULL) + { +#ifdef DEBUG + printf("th_write(): using e4crypt_policy %s\n", + t->th_buf.e4crypt_policy); +#endif + /* save old size and type */ + type2 = t->th_buf.typeflag; + sz2 = th_get_size(t); + + /* write out initial header block with fake size and type */ + t->th_buf.typeflag = TH_POL_TYPE; + + /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *content* *newline* */ + // size newline + sz = E4CRYPT_TAG_LEN + EXT4_KEY_DESCRIPTOR_HEX + 3 + 1; + + if(sz >= 100) // another ascci digit for size + ++sz; + + if(sz >= T_BLOCKSIZE) // impossible + { + errno = EINVAL; + return -1; + } + + th_set_size(t, sz); + th_finish(t); + i = tar_block_write(t, &(t->th_buf)); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + + memset(buf, 0, T_BLOCKSIZE); + snprintf(buf, T_BLOCKSIZE, "%d "E4CRYPT_TAG"%s\n", (int)sz, t->th_buf.e4crypt_policy); + i = tar_block_write(t, &buf); + if (i != T_BLOCKSIZE) + { + if (i != -1) + errno = EINVAL; + return -1; + } + + /* reset type and size to original values */ + t->th_buf.typeflag = type2; + th_set_size(t, sz2); + } +#endif + th_finish(t); #ifdef DEBUG diff --git a/libtar/extract.c b/libtar/extract.c index 6a63ff738..ba29a7771 100644 --- a/libtar/extract.c +++ b/libtar/extract.c @@ -32,6 +32,10 @@ # include "selinux/selinux.h" #endif +#ifdef HAVE_EXT4_CRYPT +# include "ext4crypt_tar.h" +#endif + const unsigned long long progress_size = (unsigned long long)(T_BLOCKSIZE); static int @@ -492,7 +496,7 @@ tar_extract_dir(TAR *t, const char *realname) } else { -#ifdef DEBUG +#if 1 //def DEBUG puts(" *** using existing directory"); #endif return 1; @@ -507,6 +511,28 @@ tar_extract_dir(TAR *t, const char *realname) } } +#ifdef HAVE_EXT4_CRYPT + if(t->th_buf.e4crypt_policy != NULL) + { +#ifdef DEBUG + printf("tar_extract_file(): restoring EXT4 crypt policy %s to dir %s\n", t->th_buf.e4crypt_policy, realname); +#endif + char binary_policy[EXT4_KEY_DESCRIPTOR_SIZE]; + if (!lookup_ref_tar(t->th_buf.e4crypt_policy, &binary_policy)) { + printf("error looking up proper e4crypt policy for '%s' - %s\n", realname, t->th_buf.e4crypt_policy); + return -1; + } + char policy_hex[EXT4_KEY_DESCRIPTOR_HEX]; + policy_to_hex(binary_policy, policy_hex); + printf("restoring policy %s > '%s' to '%s'\n", t->th_buf.e4crypt_policy, policy_hex, realname); + if (!e4crypt_policy_set(realname, binary_policy, EXT4_KEY_DESCRIPTOR_SIZE, 0)) + { + printf("tar_extract_file(): failed to restore EXT4 crypt policy %s to dir '%s' '%s'!!!\n", t->th_buf.e4crypt_policy, realname, policy_hex); + //return -1; // This may not be an error in some cases, so log and ignore + } + } +#endif + return 0; } diff --git a/libtar/libtar.h b/libtar/libtar.h index 4a513754f..ab5a3bede 100644 --- a/libtar/libtar.h +++ b/libtar/libtar.h @@ -19,6 +19,11 @@ #include "libtar_listhash.h" +#ifdef HAVE_EXT4_CRYPT +#define EXT4_KEY_DESCRIPTOR_SIZE 8 +#define EXT4_KEY_DESCRIPTOR_HEX 17 +#endif + #ifdef __cplusplus extern "C" { @@ -38,6 +43,7 @@ extern "C" /* extended metadata for next file - used to store selinux_context */ #define TH_EXT_TYPE 'x' +#define TH_POL_TYPE 'p' /* our version of the tar header structure */ struct tar_header @@ -64,6 +70,9 @@ struct tar_header #ifdef HAVE_SELINUX char *selinux_context; #endif +#ifdef HAVE_EXT4_CRYPT + char *e4crypt_policy; +#endif }; @@ -108,6 +117,7 @@ TAR; #define TAR_IGNORE_CRC 64 /* ignore CRC in file header */ #define TAR_STORE_SELINUX 128 /* store selinux context */ #define TAR_USE_NUMERIC_ID 256 /* favor numeric owner over names */ +#define TAR_STORE_EXT4_POL 512 /* store ext4 crypto policy */ /* this is obsolete - it's here for backwards-compatibility only */ #define TAR_IGNORE_MAGIC 0 @@ -204,6 +214,7 @@ int th_write(TAR *t); #define TH_ISLONGNAME(t) ((t)->th_buf.typeflag == GNU_LONGNAME_TYPE) #define TH_ISLONGLINK(t) ((t)->th_buf.typeflag == GNU_LONGLINK_TYPE) #define TH_ISEXTHEADER(t) ((t)->th_buf.typeflag == TH_EXT_TYPE) +#define TH_ISPOLHEADER(t) ((t)->th_buf.typeflag == TH_POL_TYPE) /* decode tar header info */ #define th_get_crc(t) oct_to_int((t)->th_buf.chksum, sizeof((t)->th_buf.chksum)) diff --git a/partition.cpp b/partition.cpp index b764d9479..5a53d619a 100644 --- a/partition.cpp +++ b/partition.cpp @@ -60,7 +60,6 @@ extern "C" { #include "gpt/gpt.h" #ifdef TW_INCLUDE_FBE #include "crypto/ext4crypt/Decrypt.h" - //#include "crypto/ext4crypt/Ext4Crypt.h" #endif #else #define CRYPT_FOOTER_OFFSET 0x4000 @@ -532,16 +531,16 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) { ExcludeAll(Mount_Point + "/unencrypted"); //ExcludeAll(Mount_Point + "/system/users/0"); // we WILL need to retain some of this if multiple users are present or we just need to delete more folders for the extra users somewhere else ExcludeAll(Mount_Point + "/misc/vold/user_keys"); - ExcludeAll(Mount_Point + "/system_ce"); - ExcludeAll(Mount_Point + "/system_de"); - ExcludeAll(Mount_Point + "/misc_ce"); - ExcludeAll(Mount_Point + "/misc_de"); + //ExcludeAll(Mount_Point + "/system_ce"); + //ExcludeAll(Mount_Point + "/system_de"); + //ExcludeAll(Mount_Point + "/misc_ce"); + //ExcludeAll(Mount_Point + "/misc_de"); ExcludeAll(Mount_Point + "/system/gatekeeper.password.key"); ExcludeAll(Mount_Point + "/system/gatekeeper.pattern.key"); ExcludeAll(Mount_Point + "/system/locksettings.db"); //ExcludeAll(Mount_Point + "/system/locksettings.db-shm"); // don't seem to need this one, but the other 2 are needed ExcludeAll(Mount_Point + "/system/locksettings.db-wal"); - ExcludeAll(Mount_Point + "/user_de"); + //ExcludeAll(Mount_Point + "/user_de"); //ExcludeAll(Mount_Point + "/misc/profiles/cur/0"); // might be important later ExcludeAll(Mount_Point + "/misc/gatekeeper"); ExcludeAll(Mount_Point + "/drm/kek.dat"); @@ -916,12 +915,9 @@ void TWPartition::Setup_Data_Media() { DataManager::SetValue("tw_has_internal", 1); DataManager::SetValue("tw_has_data_media", 1); backup_exclusions.add_absolute_dir("/data/data/com.google.android.music/files"); - backup_exclusions.add_absolute_dir(Mount_Point + "/misc/vold"); - wipe_exclusions.add_absolute_dir(Mount_Point + "/misc/vold"); - backup_exclusions.add_absolute_dir(Mount_Point + "/.layout_version"); - wipe_exclusions.add_absolute_dir(Mount_Point + "/.layout_version"); - backup_exclusions.add_absolute_dir(Mount_Point + "/system/storage.xml"); - wipe_exclusions.add_absolute_dir(Mount_Point + "/system/storage.xml"); + ExcludeAll(Mount_Point + "/misc/vold"); + ExcludeAll(Mount_Point + "/.layout_version"); + ExcludeAll(Mount_Point + "/system/storage.xml"); } else { if (Mount(true) && TWFunc::Path_Exists(Mount_Point + "/media/0")) { Storage_Path = Mount_Point + "/media/0"; @@ -929,8 +925,7 @@ void TWPartition::Setup_Data_Media() { UnMount(true); } } - backup_exclusions.add_absolute_dir(Mount_Point + "/media"); - wipe_exclusions.add_absolute_dir(Mount_Point + "/media"); + ExcludeAll(Mount_Point + "/media"); } void TWPartition::Find_Real_Block_Device(string& Block, bool Display_Error) { @@ -2180,7 +2175,7 @@ bool TWPartition::Wipe_Data_Without_Wiping_Media_Func(const string& parent __unu rmdir(dir.c_str()); } else if (de->d_type == DT_REG || de->d_type == DT_LNK || de->d_type == DT_FIFO || de->d_type == DT_SOCK) { if (!unlink(dir.c_str())) - LOGINFO("Unable to unlink '%s'\n", dir.c_str()); + LOGINFO("Unable to unlink '%s': %s\n", dir.c_str(), strerror(errno)); } } closedir(d); diff --git a/twrpTar.cpp b/twrpTar.cpp index 3c07a97d1..4f9b63303 100644 --- a/twrpTar.cpp +++ b/twrpTar.cpp @@ -53,6 +53,13 @@ extern "C" { #include "set_metadata.h" #endif //ndef BUILD_TWRPTAR_MAIN +#ifdef TW_INCLUDE_FBE +#include "crypto/ext4crypt/ext4crypt_tar.h" +#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_EXT4_POL +#else +#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX +#endif + using namespace std; twrpTar::twrpTar(void) { @@ -72,6 +79,9 @@ twrpTar::twrpTar(void) { input_fd = -1; output_fd = -1; backup_exclusions = NULL; +#ifdef TW_INCLUDE_FBE + e4crypt_set_mode(); +#endif } twrpTar::~twrpTar(void) { @@ -869,10 +879,10 @@ void* twrpTar::extractMulti(void *cookie) { int twrpTar::addFilesToExistingTar(vector files, string fn) { char* charTarFile = (char*) fn.c_str(); - if (tar_open(&t, charTarFile, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1) + if (tar_open(&t, charTarFile, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) == -1) return -1; removeEOT(charTarFile); - if (tar_open(&t, charTarFile, NULL, O_WRONLY | O_APPEND | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1) + if (tar_open(&t, charTarFile, NULL, O_WRONLY | O_APPEND | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) == -1) return -1; for (unsigned int i = 0; i < files.size(); ++i) { char* file = (char*) files.at(i).c_str(); @@ -977,7 +987,7 @@ int twrpTar::createTar() { fd = pipes[1]; init_libtar_no_buffer(progress_pipe_fd); tar_type.writefunc = write_tar_no_buffer; - if(tar_fdopen(&t, fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { + if(tar_fdopen(&t, fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) != 0) { close(fd); LOGINFO("tar_fdopen failed\n"); gui_err("backup_error=Error creating backup."); @@ -1037,7 +1047,7 @@ int twrpTar::createTar() { fd = pigzfd[1]; // copy parent output init_libtar_no_buffer(progress_pipe_fd); tar_type.writefunc = write_tar_no_buffer; - if(tar_fdopen(&t, fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { + if(tar_fdopen(&t, fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) != 0) { close(fd); LOGINFO("tar_fdopen failed\n"); gui_err("backup_error=Error creating backup."); @@ -1087,7 +1097,7 @@ int twrpTar::createTar() { fd = oaesfd[1]; // copy parent output init_libtar_no_buffer(progress_pipe_fd); tar_type.writefunc = write_tar_no_buffer; - if(tar_fdopen(&t, fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { + if(tar_fdopen(&t, fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) != 0) { close(fd); LOGINFO("tar_fdopen failed\n"); gui_err("backup_error=Error creating backup."); @@ -1102,7 +1112,7 @@ int twrpTar::createTar() { LOGINFO("Opening TW_ADB_BACKUP uncompressed stream\n"); tar_type.writefunc = write_tar_no_buffer; output_fd = open(TW_ADB_BACKUP, O_WRONLY); - if(tar_fdopen(&t, output_fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { + if(tar_fdopen(&t, output_fd, charRootDir, &tar_type, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) != 0) { close(output_fd); LOGERR("tar_fdopen failed\n"); return -1; @@ -1110,7 +1120,7 @@ int twrpTar::createTar() { } else { tar_type.writefunc = write_tar; - if (tar_open(&t, charTarFile, &tar_type, O_WRONLY | O_CREAT | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) == -1) { + if (tar_open(&t, charTarFile, &tar_type, O_WRONLY | O_CREAT | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) == -1) { LOGERR("tar_open error opening '%s'\n", tarfn.c_str()); gui_err("backup_error=Error creating backup."); return -1; @@ -1210,7 +1220,7 @@ int twrpTar::openTar() { close(pipes[1]); close(pipes[3]); fd = pipes[2]; - if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { + if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) != 0) { close(fd); LOGINFO("tar_fdopen failed\n"); gui_err("restore_error=Error during restore process."); @@ -1260,7 +1270,7 @@ int twrpTar::openTar() { // Parent close(oaesfd[1]); // close parent output fd = oaesfd[0]; // copy parent input - if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { + if(tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) != 0) { close(fd); LOGINFO("tar_fdopen failed\n"); gui_err("restore_error=Error during restore process."); @@ -1314,7 +1324,7 @@ int twrpTar::openTar() { // Parent close(pigzfd[1]); // close parent output fd = pigzfd[0]; // copy parent input - if (tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { + if (tar_fdopen(&t, fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) != 0) { close(fd); LOGINFO("tar_fdopen failed\n"); gui_err("restore_error=Error during restore process."); @@ -1325,14 +1335,14 @@ int twrpTar::openTar() { if (part_settings->adbbackup) { LOGINFO("Opening TW_ADB_RESTORE uncompressed stream\n"); input_fd = open(TW_ADB_RESTORE, O_RDONLY); - if (tar_fdopen(&t, input_fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { + if (tar_fdopen(&t, input_fd, charRootDir, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) != 0) { LOGERR("Unable to open tar archive '%s'\n", charTarFile); gui_err("restore_error=Error during restore process."); return -1; } } else { - if (tar_open(&t, charTarFile, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TAR_GNU | TAR_STORE_SELINUX) != 0) { + if (tar_open(&t, charTarFile, NULL, O_RDONLY | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, TWTAR_FLAGS) != 0) { LOGERR("Unable to open tar archive '%s'\n", charTarFile); gui_err("restore_error=Error during restore process."); return -1; -- cgit v1.2.3