diff --git a/Android.mk b/Android.mk index 71b2b55e..a750252f 100755 --- a/Android.mk +++ b/Android.mk @@ -84,6 +84,11 @@ LOCAL_SRC_FILES := \ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) LOCAL_STATIC_LIBRARIES += libavb LOCAL_SHARED_LIBRARIES += libfs_mgr libinit + ifeq ($(TW_INCLUDE_CRYPTO),true) + LOCAL_CFLAGS += -DUSE_FSCRYPT -Wno-macro-redefined + LOCAL_C_INCLUDES += bootable/recovery/crypto/fscrypt \ + bootable/recovery/crypto + endif LOCAL_C_INCLUDES += \ system/core/fs_mgr/libfs_avb/include/ \ system/core/fs_mgr/include_fstab/ \ @@ -91,7 +96,8 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) system/core/fs_mgr/libdm/include/ \ system/core/fs_mgr/liblp/include/ \ system/gsid/include/ \ - system/core/init/ + system/core/init/ \ + system/extras/ext4_utils/include endif ifneq ($(TARGET_RECOVERY_REBOOT_SRC),) @@ -103,7 +109,7 @@ LOCAL_MODULE := recovery RECOVERY_API_VERSION := 3 RECOVERY_FSTAB_VERSION := 2 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) -LOCAL_CFLAGS += -Wno-unused-parameter +LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function LOCAL_CLANG := true LOCAL_C_INCLUDES += \ @@ -348,7 +354,11 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0) TW_INCLUDE_CRYPTO_FBE := true LOCAL_CFLAGS += -DTW_INCLUDE_FBE - LOCAL_SHARED_LIBRARIES += libe4crypt + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) + LOCAL_SHARED_LIBRARIES += libtwrpfscrypt + else + LOCAL_SHARED_LIBRARIES += libe4crypt + endif ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) LOCAL_CFLAGS += -DTW_INCLUDE_FBE_METADATA_DECRYPT endif @@ -439,7 +449,7 @@ endif TWRP_REQUIRED_MODULES += \ relink \ - relink_init \ + twrp_ramdisk \ dump_image \ erase_image \ flash_image \ @@ -456,7 +466,20 @@ TWRP_REQUIRED_MODULES += \ init.recovery.hlthchrg.rc \ init.recovery.service.rc \ init.recovery.ldconfig.rc \ - awk + awk \ + +ifneq ($(TW_INCLUDE_CRYPTO),) +TWRP_REQUIRED_MODULES += \ + plat_service_contexts \ + plat_hwservice_contexts \ + vendor_hwservice_contexts \ + vndservice_contexts \ + hwservicemanager \ + servicemanager \ + vndservicemanager \ + vold_prepare_subdirs \ + fscryptpolicyget +endif ifneq ($(TARGET_ARCH), arm64) ifneq ($(TARGET_ARCH), x86_64) @@ -695,7 +718,7 @@ LOCAL_MODULE := librecovery LOCAL_STATIC_LIBRARIES := \ libminui \ libotautil \ - libvintf_recovery \ + libvintf \ libcrypto_utils \ libcrypto \ libbase \ @@ -720,7 +743,7 @@ else install/set_metadata.cpp verifier28/verifier.cpp install/zipwrap.cpp install/ZipUtil.cpp endif LOCAL_SHARED_LIBRARIES += libbase libbootloader_message libcrypto libext4_utils \ - libfs_mgr libfusesideload libhidl-gen-utils libhidlbase libhidltransport \ + libfs_mgr libfusesideload libhidl-gen-utils libhidlbase \ liblog libselinux libtinyxml2 libutils libz libziparchive libcutils LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) @@ -875,7 +898,11 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) include $(commands_TWRP_local_path)/crypto/fde/Android.mk include $(commands_TWRP_local_path)/crypto/scrypt/Android.mk ifeq ($(TW_INCLUDE_CRYPTO_FBE), true) - include $(commands_TWRP_local_path)/crypto/ext4crypt/Android.mk + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) + include $(commands_TWRP_local_path)/crypto/fscrypt/Android.mk + else + include $(commands_TWRP_local_path)/crypto/ext4crypt/Android.mk + endif endif ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),) ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false) diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk index c8283dd8..955bd3b8 100755 --- a/crypto/ext4crypt/Android.mk +++ b/crypto/ext4crypt/Android.mk @@ -25,7 +25,6 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) LOCAL_CFLAGS += -DHAVE_GATEKEEPER1 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) LOCAL_SHARED_LIBRARIES += android.hardware.confirmationui@1.0 - # LOCAL_CFLAGS += -DUSE_ endif LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0 ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) diff --git a/crypto/fde/Android.mk b/crypto/fde/Android.mk old mode 100644 new mode 100755 index fcdd5643..4fd8b0b7 --- a/crypto/fde/Android.mk +++ b/crypto/fde/Android.mk @@ -17,7 +17,13 @@ endif ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) #8.0 or higher LOCAL_C_INCLUDES += external/boringssl/src/include - LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite libe4crypt android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) + LOCAL_SHARED_LIBRARIES += libtwrpfscrypt + else + LOCAL_SHARED_LIBRARIES += libe4crypt + endif + LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite \ + android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) #9.0 rules LOCAL_CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment @@ -72,7 +78,13 @@ endif ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) #8.0 or higher LOCAL_C_INCLUDES += external/boringssl/src/include - LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite libe4crypt android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) + LOCAL_SHARED_LIBRARIES += libtwrpfscrypt + else + LOCAL_SHARED_LIBRARIES += libe4crypt + endif + LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite \ + android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) #9.0 rules LOCAL_CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment diff --git a/crypto/fscrypt/Android.mk b/crypto/fscrypt/Android.mk new file mode 100755 index 00000000..8000d5e1 --- /dev/null +++ b/crypto/fscrypt/Android.mk @@ -0,0 +1,82 @@ +LOCAL_PATH := $(call my-dir) +ifeq ($(TW_INCLUDE_CRYPTO), true) +include $(CLEAR_VARS) + +LOCAL_MODULE := libtwrpfscrypt +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment -Wno-missing-field-initializers \ + -DHAVE_LIBKEYUTILS -std=gnu++2a -Wno-macro-redefined -Wno-unused-function +LOCAL_SRC_FILES := Decrypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp \ + FsCrypt.cpp KeyUtil.cpp Keymaster.cpp KeyStorage.cpp MetadataCrypt.cpp KeyBuffer.cpp \ + Process.cpp EncryptInplace.cpp Weaver1.cpp fscrypt_policy.cpp +LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils \ +libkeymaster_messages libhardware libprotobuf-cpp-lite libfscrypt android.hardware.confirmationui@1.0 \ +android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0 \ +libfs_mgr android.hardware.keymaster@4.0 libkeymaster4support libf2fs_sparseblock libkeystore_parcelables \ +libkeystore_aidl android.hardware.weaver@1.0 libkeyutils liblog libhwbinder libchrome +LOCAL_STATIC_LIBRARIES := libscrypt_static +LOCAL_C_INCLUDES := system/extras/ext4_utils \ + system/extras/ext4_utils/include/ext4_utils \ + external/scrypt/lib/crypto \ + system/security/keystore/include \ + hardware/libhardware/include/hardware \ + system/security/softkeymaster/include/keymaster \ + system/keymaster/include \ + system/extras/libfscrypt/include \ + system/core/fs_mgr/libfs_avb/include/ \ + system/core/fs_mgr/include_fstab/ \ + system/core/fs_mgr/include/ \ + system/core/fs_mgr/libdm/include/ \ + system/core/fs_mgr/liblp/include/ \ + system/gsid/include/ \ + system/core/init/ \ + system/vold/model \ + system/vold/ \ + system/extras/f2fs_utils/ + +ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),) + LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX + LOCAL_C_INCLUDES += external/boringssl/src/include +endif + +LOCAL_REQUIRED_MODULES := keystore_auth keystore +LOCAL_CLANG := true +include $(BUILD_SHARED_LIBRARY) + + + +include $(CLEAR_VARS) +LOCAL_MODULE := twrpfbe +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := main.cpp +LOCAL_SHARED_LIBRARIES := libtwrpfscrypt + +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_MODULE := fscryptpolicyget +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := fscryptpolicyget.cpp +LOCAL_SHARED_LIBRARIES := libtwrpfscrypt +LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_MODULE := keystore_auth +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := keystore_auth.cpp +LOCAL_SHARED_LIBRARIES := libc libkeystore_binder libutils libbinder liblog +LOCAL_CFLAGS += -DUSE_SECURITY_NAMESPACE +LOCAL_SHARED_LIBRARIES += libkeystore_aidl +LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + +include $(BUILD_EXECUTABLE) + +endif diff --git a/crypto/fscrypt/Checkpoint.h b/crypto/fscrypt/Checkpoint.h new file mode 100644 index 00000000..63ead837 --- /dev/null +++ b/crypto/fscrypt/Checkpoint.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 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 _CHECKPOINT_H +#define _CHECKPOINT_H + +#include +#include + +namespace android { +namespace vold { + +android::binder::Status cp_supportsCheckpoint(bool& result); + +android::binder::Status cp_supportsBlockCheckpoint(bool& result); + +android::binder::Status cp_supportsFileCheckpoint(bool& result); + +android::binder::Status cp_startCheckpoint(int retry); + +android::binder::Status cp_commitChanges(); + +void cp_abortChanges(const std::string& message, bool retry); + +bool cp_needsRollback(); + +bool cp_needsCheckpoint(); + +android::binder::Status cp_prepareCheckpoint(); + +android::binder::Status cp_restoreCheckpoint(const std::string& mountPoint, int count = 0); + +android::binder::Status cp_markBootAttempt(); + +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/fscrypt/Decrypt.cpp b/crypto/fscrypt/Decrypt.cpp new file mode 100755 index 00000000..b1a43c93 --- /dev/null +++ b/crypto/fscrypt/Decrypt.cpp @@ -0,0 +1,1169 @@ +/* + * Copyright (C) 2016 - 2020 The TeamWin 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 "Decrypt.h" +#include "FsCrypt.h" + +#include +#include + +#include +#include +#include +#include +#include + +#ifndef HAVE_LIBKEYUTILS +#include "key_control.h" +#else +#include +#endif +#include "keystore_client.pb.h" +#include "Weaver1.h" +#include "cutils/properties.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "keystore_client.pb.h" + +#include +#include + +extern "C" { +#include "crypto_scrypt.h" +} + +#include "fscrypt_policy.h" +#include "HashPassword.h" +#include "KeyStorage.h" + +using android::security::keystore::IKeystoreService; +using keystore::KeystoreResponsePromise; +using keystore::OperationResultPromise; +using android::security::keymaster::OperationResult; + +// 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; + +inline std::string hidlVec2String(const ::keystore::hidl_vec& value) { + return std::string(reinterpret_cast(&value[0]), value.size()); +} + +static bool lookup_ref_key_internal(std::map& key_map, const uint8_t* policy, userid_t* user_id) { + char policy_string_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + char key_map_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(policy, policy_string_hex); + + for (std::map::iterator it=key_map.begin(); it!=key_map.end(); ++it) { + policy_to_hex(reinterpret_cast(&it->second[0]), key_map_hex); + std::string key_map_hex_string = std::string(key_map_hex); + if (key_map_hex_string == policy_string_hex) { + *user_id = it->first; + return true; + } + } + return false; +} + +extern "C" bool lookup_ref_key(const uint8_t* policy, uint8_t* policy_type) { + userid_t user_id = 0; + char policy_string_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + char de_raw_ref_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(policy, policy_string_hex); + policy_to_hex(reinterpret_cast(&de_raw_ref[0]), de_raw_ref_hex); + std::string de_raw_ref_hex_string = std::string(de_raw_ref_hex); + + std::string policy_type_string; + if (policy_string_hex == de_raw_ref_hex_string) { + policy_type_string = "0DK"; + memcpy(policy_type, policy_type_string.data(), policy_type_string.size()); + 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 + policy_type_string = "0CE" + std::to_string(user_id); + } else + policy_type_string = "0DE" + std::to_string(user_id); + memcpy(policy_type, policy_type_string.data(), policy_type_string.size()); + return true; +} + +extern "C" bool lookup_ref_tar(const uint8_t* policy_type, uint8_t* policy) { + std::string policy_type_string = std::string((char *) policy_type); + char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(policy_type, policy_hex); + + // Current encryption fscrypt policy is v1 (which is stored as version 0e) + if (policy_type_string.substr(0,1) != "0") { + printf("Unexpected version %c\n", policy_type[0]); + return false; + } + + if (policy_type_string.substr(1, 2) == "DK") { + memcpy(policy, de_raw_ref.data(), de_raw_ref.size()); + return true; + } + + userid_t user_id = atoi(policy_type_string.substr(3, 4).c_str()); + std::string raw_ref; + + if (policy_type_string.substr(1, 1) == "D") { + if (lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref)) { + memcpy(policy, raw_ref.data(), raw_ref.size()); + } else + return false; + } else if (policy_type_string.substr(1, 1) == "C") { + if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) { + memcpy(policy, raw_ref.data(), raw_ref.size()); + } else + return false; + } else { + printf("unknown policy type '%s'\n", policy_type); + return false; + } + return true; +} + +bool Decrypt_DE() { + if (!fscrypt_initialize_systemwide_keys()) { // this deals with the overarching device encryption + printf("fscrypt_initialize_systemwide_keys returned fail\n"); + return false; + } + if (!fscrypt_init_user0()) { + printf("fscrypt_init_user0 returned fail\n"); + return false; + } + return true; +} + +// Crappy functions for debugging, please ignore unless you need to debug +// void output_hex(const std::string& in) { +// const char *buf = in.data(); +// char hex[in.size() * 2 + 1]; +// unsigned int index; +// for (index = 0; index < in.size(); index++) +// sprintf(&hex[2 * index], "%02X", buf[index]); +// printf("%s", hex); +// } + +// void output_hex(const char* buf, const int size) { +// char hex[size * 2 + 1]; +// int index; +// for (index = 0; index < size; index++) +// sprintf(&hex[2 * index], "%02X", buf[index]); +// printf("%s", hex); +// } + +// void output_hex(const unsigned char* buf, const int size) { +// char hex[size * 2 + 1]; +// int index; +// for (index = 0; index < size; index++) +// sprintf(&hex[2 * index], "%02X", buf[index]); +// printf("%s", hex); +// } + +// void output_hex(std::vector* vec) { +// char hex[3]; +// unsigned int index; +// for (index = 0; index < vec->size(); index++) { +// sprintf(&hex[0], "%02X", vec->at(index)); +// printf("%s", hex); +// } +// } + +/* An alternative is to use: + * sqlite3 /data/system/locksettings.db "SELECT value FROM locksettings WHERE name='sp-handle' AND user=0;" + * but we really don't want to include the 1.1MB libsqlite in TWRP. We scan the spblob folder for the + * password data file (*.pwd) and get the handle from the filename instead. This is a replacement for + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017 + * We never use this data as an actual long. We always use it as a string. */ +bool Find_Handle(const std::string& spblob_path, std::string& handle_str) { + DIR* dir = opendir(spblob_path.c_str()); + if (!dir) { + printf("Error opening '%s'\n", spblob_path.c_str()); + return false; + } + + struct dirent* de = 0; + + while ((de = readdir(dir)) != 0) { + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + continue; + size_t len = strlen(de->d_name); + if (len <= 4) + continue; + char* p = de->d_name; + p += len - 4; + if (strncmp(p, ".pwd", 4) == 0) { + handle_str = de->d_name; + handle_str = handle_str.substr(0, len - 4); + //*handle = strtoull(handle_str.c_str(), 0 , 16); + closedir(dir); + return true; + } + } + closedir(dir); + return false; +} + +/* This is the structure of the data in the password data (*.pwd) file which the structure can be found + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#187 */ +struct password_data_struct { + int password_type; + unsigned char scryptN; + unsigned char scryptR; + unsigned char scryptP; + int salt_len; + void* salt; + int handle_len; + void* password_handle; +}; + +/* C++ replacement for + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#764 */ +bool Get_Password_Data(const std::string& spblob_path, const std::string& handle_str, password_data_struct *pwd) { + std::string pwd_file = spblob_path + handle_str + ".pwd"; + std::string pwd_data; + if (!android::base::ReadFileToString(pwd_file, &pwd_data)) { + printf("Failed to read '%s'\n", pwd_file.c_str()); + return false; + } + // output_hex(pwd_data.data(), pwd_data.size());printf("\n"); + const int* intptr = (const int*)pwd_data.data(); + pwd->password_type = *intptr; + endianswap(&pwd->password_type); + //printf("password type %i\n", pwd->password_type); // 2 was PIN, 1 for pattern, 2 also for password, -1 for default password + const unsigned char* byteptr = (const unsigned char*)pwd_data.data() + sizeof(int); + pwd->scryptN = *byteptr; + byteptr++; + pwd->scryptR = *byteptr; + byteptr++; + pwd->scryptP = *byteptr; + byteptr++; + intptr = (const int*)byteptr; + pwd->salt_len = *intptr; + endianswap(&pwd->salt_len); + if (pwd->salt_len != 0) { + pwd->salt = malloc(pwd->salt_len); + if (!pwd->salt) { + printf("Get_Password_Data malloc salt\n"); + return false; + } + memcpy(pwd->salt, intptr + 1, pwd->salt_len); + intptr++; + byteptr = (const unsigned char*)intptr; + byteptr += pwd->salt_len; + } else { + printf("Get_Password_Data salt_len is 0\n"); + return false; + } + intptr = (const int*)byteptr; + pwd->handle_len = *intptr; + endianswap(&pwd->handle_len); + if (pwd->handle_len != 0) { + pwd->password_handle = malloc(pwd->handle_len); + if (!pwd->password_handle) { + printf("Get_Password_Data malloc password_handle\n"); + return false; + } + memcpy(pwd->password_handle, intptr + 1, pwd->handle_len); + } else { + printf("Get_Password_Data handle_len is 0\n"); + // Not an error if using weaver + } + return true; +} + +/* C++ replacement for + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#765 + * called here + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1050 */ +bool Get_Password_Token(const password_data_struct *pwd, const std::string& Password, unsigned char* password_token) { + if (!password_token) { + printf("password_token is null\n"); + return false; + } + unsigned int N = 1 << pwd->scryptN; + unsigned int r = 1 << pwd->scryptR; + unsigned int p = 1 << pwd->scryptP; + //printf("N %i r %i p %i\n", N, r, p); + int ret = crypto_scrypt(reinterpret_cast(Password.data()), Password.size(), + reinterpret_cast(pwd->salt), pwd->salt_len, + N, r, p, + password_token, 32); + if (ret != 0) { + printf("scrypt error\n"); + return false; + } + return true; +} + +// Data structure for the *.weaver file, see Get_Weaver_Data below +struct weaver_data_struct { + unsigned char version; + int slot; +}; + +/* C++ replacement for + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#501 + * called here + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768 */ +bool Get_Weaver_Data(const std::string& spblob_path, const std::string& handle_str, weaver_data_struct *wd) { + std::string weaver_file = spblob_path + handle_str + ".weaver"; + std::string weaver_data; + if (!android::base::ReadFileToString(weaver_file, &weaver_data)) { + printf("Failed to read '%s'\n", weaver_file.c_str()); + return false; + } + // output_hex(weaver_data.data(), weaver_data.size());printf("\n"); + const unsigned char* byteptr = (const unsigned char*)weaver_data.data(); + wd->version = *byteptr; + // printf("weaver version %i\n", wd->version); + const int* intptr = (const int*)weaver_data.data() + sizeof(unsigned char); + wd->slot = *intptr; + //endianswap(&wd->slot); not needed + // printf("weaver slot %i\n", wd->slot); + return true; +} + +namespace android { + +// On Android 8.0 for some reason init can't seem to completely stop keystore +// so we have to kill it too if it doesn't die on its own. +static void kill_keystore() { + DIR* dir = opendir("/proc"); + if (dir) { + struct dirent* de = 0; + + while ((de = readdir(dir)) != 0) { + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + continue; + + int pid = -1; + int ret = sscanf(de->d_name, "%d", &pid); + + if (ret == 1) { + char cmdpath[PATH_MAX]; + sprintf(cmdpath, "/proc/%d/cmdline", pid); + + FILE* file = fopen(cmdpath, "r"); + size_t task_size = PATH_MAX; + char task[PATH_MAX]; + char* p = task; + if (getline(&p, &task_size, file) > 0) { + if (strstr(task, "keystore") != 0) { + printf("keystore pid %d found, sending kill.\n", pid); + kill(pid, SIGINT); + usleep(5000); + kill(pid, SIGKILL); + } + } + fclose(file); + } + } + closedir(dir); + } +} + +// The keystore holds a file open on /data so we have to stop / kill it +// if we want to be able to unmount /data for things like formatting. +static void stop_keystore() { + printf("Stopping keystore...\n"); + property_set("ctl.stop", "keystore"); + usleep(5000); + kill_keystore(); +} + +/* These next 2 functions try to get the keystore service 50 times because + * the keystore is not always ready when TWRP boots */ +android::sp getKeystoreBinder() { + android::sp sm = android::defaultServiceManager(); + return sm->getService(String16("android.security.keystore")); +} + +android::sp getKeystoreBinderRetry() { + printf("Starting keystore...\n"); + property_set("ctl.start", "keystore"); + int retry_count = 50; + android::sp binder = getKeystoreBinder(); + while (binder == NULL && retry_count) { + printf("Waiting for keystore service... %i\n", retry_count--); + sleep(1); + binder = getKeystoreBinder(); + } + return binder; +} + +namespace keystore { + +#define SYNTHETIC_PASSWORD_VERSION_V1 1 +#define SYNTHETIC_PASSWORD_VERSION_V2 2 +#define SYNTHETIC_PASSWORD_VERSION_V3 3 +#define SYNTHETIC_PASSWORD_PASSWORD_BASED 0 +#define SYNTHETIC_PASSWORD_KEY_PREFIX "USRSKEY_synthetic_password_" +#define USR_PRIVATE_KEY_PREFIX "USRPKEY_synthetic_password_" + +static std::string mKey_Prefix; + +/* The keystore alias subid is sometimes the same as the handle, but not always. + * In the case of handle 0c5303fd2010fe29, the alias subid used c5303fd2010fe29 + * without the leading 0. We could try to parse the data from a previous + * keystore request, but I think this is an easier solution because there + * is little to no documentation on the format of data we get back from + * the keystore in this instance. We also want to copy everything to a temp + * folder so that any key upgrades that might take place do not actually + * upgrade the keys on the data partition. We rename all 1000 uid files to 0 + * to pass the keystore permission checks. */ +bool Find_Keystore_Alias_SubID_And_Prep_Files(const userid_t user_id, std::string& keystoreid, const std::string& handle_str) { + char path_c[PATH_MAX]; + sprintf(path_c, "/data/misc/keystore/user_%d", user_id); + char user_dir[PATH_MAX]; + sprintf(user_dir, "user_%d", user_id); + std::string source_path = "/data/misc/keystore/"; + source_path += user_dir; + std::string handle_sub = handle_str; + while (handle_sub.substr(0,1) == "0") { + std::string temp = handle_sub.substr(1); + handle_sub = temp; + } + mKey_Prefix = ""; + + mkdir("/tmp/misc", 0755); + mkdir("/tmp/misc/keystore", 0755); + std::string destination_path = "/tmp/misc/keystore/"; + destination_path += user_dir; + if (mkdir(destination_path.c_str(), 0755) && errno != EEXIST) { + printf("failed to mkdir '%s' %s\n", destination_path.c_str(), strerror(errno)); + return false; + } + destination_path += "/"; + + DIR* dir = opendir(source_path.c_str()); + if (!dir) { + printf("Error opening '%s'\n", source_path.c_str()); + return false; + } + source_path += "/"; + + struct dirent* de = 0; + size_t prefix_len = strlen(SYNTHETIC_PASSWORD_KEY_PREFIX); + bool found_subid = false; + bool has_pkey = false; // PKEY has priority over SKEY + + while ((de = readdir(dir)) != 0) { + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + continue; + if (!found_subid) { + size_t len = strlen(de->d_name); + if (len <= prefix_len) + continue; + if (strstr(de->d_name, SYNTHETIC_PASSWORD_KEY_PREFIX) && !has_pkey) + mKey_Prefix = SYNTHETIC_PASSWORD_KEY_PREFIX; + else if (strstr(de->d_name, USR_PRIVATE_KEY_PREFIX)) { + mKey_Prefix = USR_PRIVATE_KEY_PREFIX; + has_pkey = true; + } else + continue; + if (strstr(de->d_name, handle_sub.c_str())) { + keystoreid = handle_sub; + printf("keystoreid matched handle_sub: '%s'\n", keystoreid.c_str()); + found_subid = true; + } else { + std::string file = de->d_name; + std::size_t found = file.find_last_of("_"); + if (found != std::string::npos) { + keystoreid = file.substr(found + 1); + // printf("possible keystoreid: '%s'\n", keystoreid.c_str()); + //found_subid = true; // we'll keep going in hopes that we find a pkey or a match to the handle_sub + } + } + } + std::string src = source_path; + src += de->d_name; + std::ifstream srcif(src.c_str(), std::ios::binary); + std::string dst = destination_path; + dst += de->d_name; + std::size_t source_uid = dst.find("1000"); + if (source_uid != std::string::npos) + dst.replace(source_uid, 4, "0"); + std::ofstream dstof(dst.c_str(), std::ios::binary); + printf("copying '%s' to '%s'\n", src.c_str(), dst.c_str()); + dstof << srcif.rdbuf(); + srcif.close(); + dstof.close(); + } + closedir(dir); + if (!found_subid && !mKey_Prefix.empty() && !keystoreid.empty()) + found_subid = true; + return found_subid; +} + +/* C++ replacement for function of the same name + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#867 + * returning an empty string indicates an error */ +std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const std::string& handle_str, const userid_t user_id, + const void* application_id, const size_t application_id_size, uint32_t auth_token_len) { + std::string disk_decryption_secret_key = ""; + + android::ProcessState::self()->startThreadPool(); + + std::string keystore_alias_subid; + if (!Find_Keystore_Alias_SubID_And_Prep_Files(user_id, keystore_alias_subid, handle_str)) { + printf("failed to scan keystore alias subid and prep keystore files\n"); + return disk_decryption_secret_key; + } + + // First get the keystore service + android::sp binder = getKeystoreBinderRetry(); + android::sp service = interface_cast(binder); + + if (service == NULL) { + printf("error: could not connect to keystore service\n"); + return disk_decryption_secret_key; + } + + if (auth_token_len > 0) { + printf("Starting keystore_auth service...\n"); + property_set("ctl.start", "keystore_auth"); + } + + // Read the data from the .spblob file per: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#869 + std::string spblob_file = spblob_path + handle_str + ".spblob"; + std::string spblob_data; + if (!android::base::ReadFileToString(spblob_file, &spblob_data)) { + printf("Failed to read '%s'\n", spblob_file.c_str()); + return disk_decryption_secret_key; + } + unsigned char* byteptr = (unsigned char*)spblob_data.data(); + if (*byteptr != SYNTHETIC_PASSWORD_VERSION_V2 && *byteptr != SYNTHETIC_PASSWORD_VERSION_V1 + && *byteptr != SYNTHETIC_PASSWORD_VERSION_V3) { + printf("Unsupported synthetic password version %i\n", *byteptr); + return disk_decryption_secret_key; + } + const unsigned char* synthetic_password_version = byteptr; + byteptr++; + if (*byteptr != SYNTHETIC_PASSWORD_PASSWORD_BASED) { + printf("spblob data is not SYNTHETIC_PASSWORD_PASSWORD_BASED\n"); + return disk_decryption_secret_key; + } + byteptr++; // Now we're pointing to the blob data itself + if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V2 + || *synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) { + printf("spblob v2 / v3\n"); + /* Version 2 / 3 of the spblob is basically the same as version 1, but the order of getting the intermediate key and disk decryption key have been flip-flopped + * as seen in https://android.googlesource.com/platform/frameworks/base/+/5025791ac6d1538224e19189397de8d71dcb1a12 + */ + /* First decrypt call found in + * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#135 + * We will use https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java + * and https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java + * First we set some algorithm parameters as seen in two places: + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#297 + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#216 */ + // When using secdis (aka not weaver) you must supply an auth token to the keystore prior to the begin operation + if (auth_token_len > 0) { + /*::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, auth_token_len); + if (!auth_result.isOk()) { + // The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0 + printf("keystore error adding auth token\n"); + return disk_decryption_secret_key; + }*/ + // The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file earlier and + // run a separate service that runs user the system user to add the auth token. We wait for the auth token file to be + // deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after after a while if + // the /auth_token file never gets deleted. + int auth_wait_count = 20; + while (access("/auth_token", F_OK) == 0 && auth_wait_count-- > 0) + usleep(5000); + if (auth_wait_count == 0 || access("/auth_error", F_OK) == 0) { + printf("error during keymaster_auth service\n"); + /* If you are getting this error, make sure that you have the keymaster_auth service defined in your init scripts, preferrably in init.recovery.{ro.hardware}.rc + * service keystore_auth /sbin/keystore_auth + * disabled + * oneshot + * user system + * group root + * seclabel u:r:recovery:s0 + * + * And check dmesg for error codes regarding this service if needed. */ + return disk_decryption_secret_key; + } + } + int32_t ret; + size_t maclen = 128; + unsigned char* iv = (unsigned char*)byteptr; // The IV is the first 12 bytes of the spblob + ::keystore::hidl_vec iv_hidlvec; + iv_hidlvec.setToExternal((unsigned char*)byteptr, 12); + // printf("iv: "); output_hex((const unsigned char*)iv, 12); printf("\n"); + std::string keystore_alias = mKey_Prefix; + keystore_alias += keystore_alias_subid; + String16 keystore_alias16(keystore_alias.data(), keystore_alias.size()); + int32_t error_code; + unsigned char* cipher_text = (unsigned char*)byteptr + 12; // The cipher text comes immediately after the IV + std::string cipher_text_str(byteptr, byteptr + spblob_data.size() - 14); + + ::keystore::hidl_vec cipher_text_hidlvec; + ::keystore::AuthorizationSetBuilder begin_params; + + cipher_text_hidlvec.setToExternal(cipher_text, spblob_data.size() - 14 /* 1 each for version and SYNTHETIC_PASSWORD_PASSWORD_BASED and 12 for the iv */); + begin_params.Authorization(::keystore::TAG_ALGORITHM, ::keystore::Algorithm::AES); + begin_params.Authorization(::keystore::TAG_BLOCK_MODE, ::keystore::BlockMode::GCM); + begin_params.Padding(::keystore::PaddingMode::NONE); + begin_params.Authorization(::keystore::TAG_NONCE, iv_hidlvec); + begin_params.Authorization(::keystore::TAG_MAC_LENGTH, maclen); + + ::keystore::hidl_vec entropy; // No entropy is needed for decrypt + entropy.resize(0); + android::security::keymaster::KeymasterArguments empty_params; + android::hardware::keymaster::V4_0::KeyPurpose decryptPurpose = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT; + android::sp decryptAuthToken(new android::BBinder); + + android::sp promise = new OperationResultPromise; + auto future = promise->get_future(); + auto binder_result = service->begin(promise, decryptAuthToken, keystore_alias16, (int32_t)decryptPurpose, true, + android::security::keymaster::KeymasterArguments(begin_params.hidl_data()), + entropy, -1, &error_code); + if (!binder_result.isOk()) { + printf("communication error while calling keystore\n"); + return disk_decryption_secret_key; + } + ::keystore::KeyStoreNativeReturnCode rc(error_code); + if (!rc.isOk()) { + printf("Keystore begin returned: %u\n", error_code); + return disk_decryption_secret_key; + } + OperationResult result = future.get(); + auto handle = std::move(result.token); + + // The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64 + // See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208 + future = {}; + promise = new OperationResultPromise(); + future = promise->get_future(); + binder_result = service->update(promise, handle, empty_params, cipher_text_hidlvec, &error_code); + rc = ::keystore::KeyStoreNativeReturnCode(error_code); + if (!rc.isOk()) { + printf("Keystore update returned: %d\n", error_code); + return disk_decryption_secret_key; + } + result = future.get(); + if (!result.resultCode.isOk()) { + printf("update failed: %d\n", error_code); + return disk_decryption_secret_key; + } + + size_t keystore_result_size = result.data.size(); + unsigned char* keystore_result = (unsigned char*)malloc(keystore_result_size); + if (!keystore_result) { + printf("malloc on keystore_result\n"); + return disk_decryption_secret_key; + } + memcpy(keystore_result, &result.data[0], result.data.size()); + future = {}; + promise = new OperationResultPromise(); + future = promise->get_future(); + ::keystore::hidl_vec signature; + binder_result = service->finish(promise, handle, empty_params, signature, entropy, &error_code); + if (!binder_result.isOk()) { + printf("communication error while calling keystore\n"); + free(keystore_result); + return disk_decryption_secret_key; + } + rc = ::keystore::KeyStoreNativeReturnCode(error_code); + if (!rc.isOk()) { + printf("Keystore finish returned: %d\n", error_code); + return disk_decryption_secret_key; + } + result = future.get(); + if (!result.resultCode.isOk()) { + printf("finish failed: %d\n", error_code); + return disk_decryption_secret_key; + } + stop_keystore(); + /* Now we do the second decrypt call as seen in: + * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#136 + */ + const unsigned char* intermediate_iv = keystore_result; + // printf("intermediate_iv: "); output_hex((const unsigned char*)intermediate_iv, 12); printf("\n"); + const unsigned char* intermediate_cipher_text = (const unsigned char*)keystore_result + 12; // The cipher text comes immediately after the IV + int cipher_size = keystore_result_size - 12; + // First we personalize as seen https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#102 + void* personalized_application_id = PersonalizedHashBinary(PERSONALISATION_APPLICATION_ID, (const char*)application_id, application_id_size); + if (!personalized_application_id) { + return disk_decryption_secret_key; + } + // printf("personalized application id: "); output_hex((unsigned char*)personalized_application_id, SHA512_DIGEST_LENGTH); printf("\n"); + // Now we'll decrypt using openssl AES/GCM/NoPadding + OpenSSL_add_all_ciphers(); + int actual_size=0, final_size=0; + EVP_CIPHER_CTX *d_ctx = EVP_CIPHER_CTX_new(); + const unsigned char* key = (const unsigned char*)personalized_application_id; // The key is the now personalized copy of the application ID + // printf("key: "); output_hex((const unsigned char*)key, 32); printf("\n"); + EVP_DecryptInit(d_ctx, EVP_aes_256_gcm(), key, intermediate_iv); + unsigned char* secret_key = (unsigned char*)malloc(cipher_size); + if (!secret_key) { + printf("malloc failure on secret key\n"); + return disk_decryption_secret_key; + } + EVP_DecryptUpdate(d_ctx, secret_key, &actual_size, intermediate_cipher_text, cipher_size); + unsigned char tag[AES_BLOCK_SIZE]; + EVP_CIPHER_CTX_ctrl(d_ctx, EVP_CTRL_GCM_SET_TAG, 16, tag); + EVP_DecryptFinal_ex(d_ctx, secret_key + actual_size, &final_size); + EVP_CIPHER_CTX_free(d_ctx); + free(personalized_application_id); + free(keystore_result); + int secret_key_real_size = actual_size - 16; + // printf("secret key: "); output_hex((const unsigned char*)secret_key, secret_key_real_size); printf("\n"); + // The payload data from the keystore update is further personalized at https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153 + // We now have the disk decryption key! + if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) { + // V3 uses SP800 instead of SHA512 + disk_decryption_secret_key = PersonalizedHashSP800(PERSONALIZATION_FBE_KEY, PERSONALISATION_CONTEXT, (const char*)secret_key, secret_key_real_size); + } else { + disk_decryption_secret_key = PersonalizedHash(PERSONALIZATION_FBE_KEY, (const char*)secret_key, secret_key_real_size); + } + // printf("disk_decryption_secret_key: '%s'\n", disk_decryption_secret_key.c_str()); + free(secret_key); + return disk_decryption_secret_key; + } + return disk_decryption_secret_key; +} + +}} + +#define PASSWORD_TOKEN_SIZE 32 + +/* C++ replacement for + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#992 + * called here + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#813 */ +bool Get_Secdis(const std::string& spblob_path, const std::string& handle_str, std::string& secdis_data) { + std::string secdis_file = spblob_path + handle_str + ".secdis"; + if (!android::base::ReadFileToString(secdis_file, &secdis_data)) { + printf("Failed to read '%s'\n", secdis_file.c_str()); + return false; + } + // output_hex(secdis_data.data(), secdis_data.size());printf("\n"); + return true; +} + +// C++ replacement for https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1033 +userid_t fakeUid(const userid_t uid) { + return 100000 + uid; +} + +bool Is_Weaver(const std::string& spblob_path, const std::string& handle_str) { + std::string weaver_file = spblob_path + handle_str + ".weaver"; + struct stat st; + if (stat(weaver_file.c_str(), &st) == 0) + return true; + return false; +} + +bool Free_Return(bool retval, void* weaver_key, password_data_struct* pwd) { + if (weaver_key) + free(weaver_key); + if (pwd->salt) + free(pwd->salt); + if (pwd->password_handle) + free(pwd->password_handle); + return retval; +} + +/* Decrypt_User_Synth_Pass is the TWRP C++ equivalent to spBasedDoVerifyCredential + * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#1998 */ +bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password) { + bool retval = false; + void* weaver_key = NULL; + password_data_struct pwd; + pwd.salt = NULL; + pwd.salt_len = 0; + pwd.password_handle = NULL; + pwd.handle_len = 0; + char application_id[PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH]; + + uint32_t auth_token_len = 0; + + std::string secret; // this will be the disk decryption key that is sent to vold + std::string token = "!"; // there is no token used for this kind of decrypt, key escrow is handled by weaver + int flags = FLAG_STORAGE_DE; + if (user_id == 0) + flags = FLAG_STORAGE_DE; + else + flags = FLAG_STORAGE_CE; + char spblob_path_char[PATH_MAX]; + sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id); + std::string spblob_path = spblob_path_char; + long handle = 0; + std::string handle_str; + // Get the handle: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017 + if (!Find_Handle(spblob_path, handle_str)) { + printf("Error getting handle\n"); + return Free_Return(retval, weaver_key, &pwd); + } + // printf("Handle is '%s'\n", handle_str.c_str()); + // Now we begin driving unwrapPasswordBasedSyntheticPassword from: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#758 + // First we read the password data which contains scrypt parameters + if (!Get_Password_Data(spblob_path, handle_str, &pwd)) { + printf("Failed to Get_Password_Data\n"); + return Free_Return(retval, weaver_key, &pwd); + } + // printf("pwd N %i R %i P %i salt ", pwd.scryptN, pwd.scryptR, pwd.scryptP); output_hex((char*)pwd.salt, pwd.salt_len); printf("\n"); + unsigned char password_token[PASSWORD_TOKEN_SIZE]; + // printf("Password: '%s'\n", Password.c_str()); + // The password token is the password scrypted with the parameters from the password data file + if (!Get_Password_Token(&pwd, Password, &password_token[0])) { + printf("Failed to Get_Password_Token\n"); + return Free_Return(retval, weaver_key, &pwd); + } + // output_hex(&password_token[0], PASSWORD_TOKEN_SIZE);printf("\n"); + if (Is_Weaver(spblob_path, handle_str)) { + printf("using weaver\n"); + // BEGIN PIXEL 2 WEAVER + // Get the weaver data from the .weaver file which tells us which slot to use when we ask weaver for the escrowed key + // https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768 + weaver_data_struct wd; + if (!Get_Weaver_Data(spblob_path, handle_str, &wd)) { + printf("Failed to get weaver data\n"); + return Free_Return(retval, weaver_key, &pwd); + } + // The weaver key is the the password token prefixed with "weaver-key" padded to 128 with nulls with the password token appended then SHA512 + // https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1059 + weaver_key = PersonalizedHashBinary(PERSONALISATION_WEAVER_KEY, (char*)&password_token[0], PASSWORD_TOKEN_SIZE); + if (!weaver_key) { + printf("malloc error getting weaver_key\n"); + return Free_Return(retval, weaver_key, &pwd); + } + // Now we start driving weaverVerify: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#343 + // Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#776 + android::vold::Weaver weaver; + if (!weaver) { + printf("Failed to get weaver service\n"); + return Free_Return(retval, weaver_key, &pwd); + } + // Get the key size from weaver service + uint32_t weaver_key_size = 0; + if (!weaver.GetKeySize(&weaver_key_size)) { + printf("Failed to get weaver key size\n"); + return Free_Return(retval, weaver_key, &pwd); + } else { + printf("weaver key size is %u\n", weaver_key_size); + } + // printf("weaver key: "); output_hex((unsigned char*)weaver_key, weaver_key_size); printf("\n"); + // Send the slot from the .weaver file, the computed weaver key, and get the escrowed key data + std::vector weaver_payload; + // TODO: we should return more information about the status including time delays before the next retry + if (!weaver.WeaverVerify(wd.slot, weaver_key, &weaver_payload)) { + printf("failed to weaver verify\n"); + return Free_Return(retval, weaver_key, &pwd); + } + // printf("weaver payload: "); output_hex(&weaver_payload); printf("\n"); + // Done with weaverVerify + // Now we will compute the application ID + // https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#964 + // Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#780 + // The escrowed weaver key data is prefixed with "weaver-pwd" padded to 128 with nulls with the weaver payload appended then SHA512 + void* weaver_secret = PersonalizedHashBinary(PERSONALISATION_WEAVER_PASSWORD, (const char*)weaver_payload.data(), weaver_payload.size()); + // printf("weaver secret: "); output_hex((unsigned char*)weaver_secret, SHA512_DIGEST_LENGTH); printf("\n"); + // The application ID is the password token and weaver secret appended to each other + memcpy((void*)&application_id[0], (void*)&password_token[0], PASSWORD_TOKEN_SIZE); + memcpy((void*)&application_id[PASSWORD_TOKEN_SIZE], weaver_secret, SHA512_DIGEST_LENGTH); + // printf("application ID: "); output_hex((unsigned char*)application_id, PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH); printf("\n"); + // END PIXEL 2 WEAVER + } else { + printf("using secdis\n"); + std::string secdis_data; + if (!Get_Secdis(spblob_path, handle_str, secdis_data)) { + printf("Failed to get secdis data\n"); + return Free_Return(retval, weaver_key, &pwd); + } + void* secdiscardable = PersonalizedHashBinary(PERSONALISATION_SECDISCARDABLE, (char*)secdis_data.data(), secdis_data.size()); + if (!secdiscardable) { + printf("malloc error getting secdiscardable\n"); + return Free_Return(retval, weaver_key, &pwd); + } + memcpy((void*)&application_id[0], (void*)&password_token[0], PASSWORD_TOKEN_SIZE); + memcpy((void*)&application_id[PASSWORD_TOKEN_SIZE], secdiscardable, SHA512_DIGEST_LENGTH); + + int ret = -1; + bool request_reenroll = false; + android::sp gk_device; + gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService(); + if (gk_device == nullptr) { + printf("failed to get gatekeeper service\n"); + return Free_Return(retval, weaver_key, &pwd); + } + if (pwd.handle_len <= 0) { + printf("no password handle supplied\n"); + return Free_Return(retval, weaver_key, &pwd); + } + android::hardware::hidl_vec pwd_handle_hidl; + pwd_handle_hidl.setToExternal(const_cast((const uint8_t *)pwd.password_handle), pwd.handle_len); + void* gk_pwd_token = PersonalizedHashBinary(PERSONALIZATION_USER_GK_AUTH, (char*)&password_token[0], PASSWORD_TOKEN_SIZE); + if (!gk_pwd_token) { + printf("malloc error getting gatekeeper_key\n"); + return Free_Return(retval, weaver_key, &pwd); + } + android::hardware::hidl_vec gk_pwd_token_hidl; + gk_pwd_token_hidl.setToExternal(const_cast((const uint8_t *)gk_pwd_token), SHA512_DIGEST_LENGTH); + android::hardware::Return hwRet = + gk_device->verify(fakeUid(user_id), 0 /* challange */, + pwd_handle_hidl, + gk_pwd_token_hidl, + [&ret, &request_reenroll, &auth_token_len] + (const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) { + ret = static_cast(rsp.code); // propagate errors + if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) { + auth_token_len = rsp.data.size(); + request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL); + ret = 0; // all success states are reported as 0 + // The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file here and later + // run a separate service that runs as the system user to add the auth token. We wait for the auth token file to be + // deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after a while seconds if + // the /auth_token file never gets deleted. + unlink("/auth_token"); + FILE* auth_file = fopen("/auth_token","wb"); + if (auth_file != NULL) { + fwrite(rsp.data.data(), sizeof(uint8_t), rsp.data.size(), auth_file); + fclose(auth_file); + } else { + printf("failed to open /auth_token for writing\n"); + ret = -2; + } + } else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) { + ret = rsp.timeout; + } + } + ); + free(gk_pwd_token); + if (!hwRet.isOk() || ret != 0) { + printf("gatekeeper verification failed\n"); + return Free_Return(retval, weaver_key, &pwd); + } + } + // Now we will handle https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#816 + // Plus we will include the last bit that computes the disk decrypt key found in: + // https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153 + secret = android::keystore::unwrapSyntheticPasswordBlob(spblob_path, handle_str, user_id, (const void*)&application_id[0], + PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH, auth_token_len); + if (!secret.size()) { + printf("failed to unwrapSyntheticPasswordBlob\n"); + return Free_Return(retval, weaver_key, &pwd); + } + + if (!fscrypt_unlock_user_key(user_id, 0, token.c_str(), secret.c_str())) { + printf("fscrypt_unlock_user_key returned fail\n"); + return Free_Return(retval, weaver_key, &pwd); + } + + if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) { + printf("failed to fscrypt_prepare_user_storage\n"); + return Free_Return(retval, weaver_key, &pwd); + } + printf("Decrypted Successfully!\n"); + retval = true; + return Free_Return(retval, weaver_key, &pwd); +} + +int Get_Password_Type(const userid_t user_id, std::string& filename) { + struct stat st; + char spblob_path_char[PATH_MAX]; + sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id); + if (stat(spblob_path_char, &st) == 0) { + printf("Using synthetic password method\n"); + std::string spblob_path = spblob_path_char; + std::string handle_str; + if (!Find_Handle(spblob_path, handle_str)) { + printf("Error getting handle\n"); + return 0; + } + printf("Handle is '%s'\n", handle_str.c_str()); + password_data_struct pwd; + if (!Get_Password_Data(spblob_path, handle_str, &pwd)) { + printf("Failed to Get_Password_Data\n"); + return 0; + } + if (pwd.password_type == 1) { // In Android this means pattern + printf("password type: pattern\n"); + return 2; // In TWRP this means pattern + } + else if (pwd.password_type == 2) { // In Android this means PIN or password + printf("password type: pin\n"); + return 1; // In TWRP this means PIN or password + } + printf("using default password\n"); + return 0; // We'll try the default password + } + std::string path; + if (user_id == 0) { + path = "/data/system/"; + } else { + char user_id_str[5]; + sprintf(user_id_str, "%i", user_id); + path = "/data/system/users/"; + path += user_id_str; + path += "/"; + } + filename = path + "gatekeeper.password.key"; + if (stat(filename.c_str(), &st) == 0 && st.st_size > 0) + return 1; + filename = path + "gatekeeper.pattern.key"; + if (stat(filename.c_str(), &st) == 0 && st.st_size > 0) + return 2; + printf("Unable to locate gatekeeper password file '%s'\n", filename.c_str()); + filename = ""; + return 0; +} + +bool Decrypt_User(const userid_t user_id, const std::string& Password) { + uint8_t *auth_token; + uint32_t auth_token_len; + int ret; + + struct stat st; + if (user_id > 9999) { + printf("user_id is too big\n"); + return false; + } + std::string filename; + bool Default_Password = (Password == "!"); + if (Get_Password_Type(user_id, filename) == 0 && !Default_Password) { + printf("Unknown password type\n"); + return false; + } + int flags = FLAG_STORAGE_DE; + if (user_id == 0) + flags = FLAG_STORAGE_DE; + else + flags = FLAG_STORAGE_CE; + + if (Default_Password) { + if (!fscrypt_unlock_user_key(user_id, 0, "!", "!")) { + printf("unlock_user_key returned fail\n"); + return false; + } + if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) { + printf("failed to fscrypt_prepare_user_storage\n"); + return false; + } + printf("Decrypted Successfully!\n"); + return true; + } + if (stat("/data/system_de/0/spblob", &st) == 0) { + printf("Using synthetic password method\n"); + return Decrypt_User_Synth_Pass(user_id, Password); + } + // printf("password filename is '%s'\n", filename.c_str()); + if (stat(filename.c_str(), &st) != 0) { + printf("error stat'ing key file: %s\n", strerror(errno)); + return false; + } + std::string handle; + if (!android::base::ReadFileToString(filename, &handle)) { + printf("Failed to read '%s'\n", filename.c_str()); + return false; + } + bool should_reenroll; + bool request_reenroll = false; + android::sp gk_device; + gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService(); + if (gk_device == nullptr) + return false; + android::hardware::hidl_vec curPwdHandle; + curPwdHandle.setToExternal(const_cast((const uint8_t *)handle.c_str()), st.st_size); + android::hardware::hidl_vec enteredPwd; + enteredPwd.setToExternal(const_cast((const uint8_t *)Password.c_str()), Password.size()); + + android::hardware::Return hwRet = + gk_device->verify(user_id, 0 /* challange */, + curPwdHandle, + enteredPwd, + [&ret, &request_reenroll, &auth_token, &auth_token_len] + (const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) { + ret = static_cast(rsp.code); // propagate errors + if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) { + auth_token = new uint8_t[rsp.data.size()]; + auth_token_len = rsp.data.size(); + memcpy(auth_token, rsp.data.data(), auth_token_len); + request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL); + ret = 0; // all success states are reported as 0 + } else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) { + ret = rsp.timeout; + } + } + ); + if (!hwRet.isOk()) { + return false; + } + + char token_hex[(auth_token_len*2)+1]; + token_hex[(auth_token_len*2)] = 0; + uint32_t i; + for (i=0;i +#include + +#include + +#include + +__BEGIN_DECLS + +// NOTE: keep in sync with StorageManager +static constexpr int FLAG_STORAGE_DE = 1 << 0; +static constexpr int FLAG_STORAGE_CE = 1 << 1; +// For 9.0 Ext4CryptPie.cpp +static constexpr int STORAGE_FLAG_DE = 1 << 0; +static constexpr int STORAGE_FLAG_CE = 1 << 1; + + +int Get_Password_Type(const userid_t user_id, std::string& filename); +bool Decrypt_DE(); +bool Decrypt_User(const userid_t user_id, const std::string& Password); +__END_DECLS diff --git a/crypto/fscrypt/EncryptInplace.cpp b/crypto/fscrypt/EncryptInplace.cpp new file mode 100644 index 00000000..37557182 --- /dev/null +++ b/crypto/fscrypt/EncryptInplace.cpp @@ -0,0 +1,623 @@ +/* + * 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 "EncryptInplace.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +// HORRIBLE HACK, FIXME +#include "cryptfs.h" + +// FIXME horrible cut-and-paste code +static inline int unix_read(int fd, void* buff, int len) { + return TEMP_FAILURE_RETRY(read(fd, buff, len)); +} + +static inline int unix_write(int fd, const void* buff, int len) { + return TEMP_FAILURE_RETRY(write(fd, buff, len)); +} + +#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / CRYPT_SECTOR_SIZE) + +/* aligned 32K writes tends to make flash happy. + * SD card association recommends it. + */ +#ifndef CONFIG_HW_DISK_ENCRYPTION +#define BLOCKS_AT_A_TIME 8 +#else +#define BLOCKS_AT_A_TIME 1024 +#endif + +struct encryptGroupsData { + int realfd; + int cryptofd; + off64_t numblocks; + off64_t one_pct, cur_pct, new_pct; + off64_t blocks_already_done, tot_numblocks; + off64_t used_blocks_already_done, tot_used_blocks; + const char* real_blkdev; + const char* crypto_blkdev; + int count; + off64_t offset; + char* buffer; + off64_t last_written_sector; + int completed; + time_t time_started; + int remaining_time; + bool set_progress_properties; +}; + +static void update_progress(struct encryptGroupsData* data, int is_used) { + data->blocks_already_done++; + + if (is_used) { + data->used_blocks_already_done++; + } + if (data->tot_used_blocks) { + data->new_pct = data->used_blocks_already_done / data->one_pct; + } else { + data->new_pct = data->blocks_already_done / data->one_pct; + } + + if (!data->set_progress_properties) return; + + if (data->new_pct > data->cur_pct) { + char buf[8]; + data->cur_pct = data->new_pct; + snprintf(buf, sizeof(buf), "%" PRId64, data->cur_pct); + android::base::SetProperty("vold.encrypt_progress", buf); + } + + if (data->cur_pct >= 5) { + struct timespec time_now; + if (clock_gettime(CLOCK_MONOTONIC, &time_now)) { + LOG(WARNING) << "Error getting time"; + } else { + double elapsed_time = difftime(time_now.tv_sec, data->time_started); + off64_t remaining_blocks = data->tot_used_blocks - data->used_blocks_already_done; + int remaining_time = + (int)(elapsed_time * remaining_blocks / data->used_blocks_already_done); + + // Change time only if not yet set, lower, or a lot higher for + // best user experience + if (data->remaining_time == -1 || remaining_time < data->remaining_time || + remaining_time > data->remaining_time + 60) { + char buf[8]; + snprintf(buf, sizeof(buf), "%d", remaining_time); + android::base::SetProperty("vold.encrypt_time_remaining", buf); + data->remaining_time = remaining_time; + } + } + } +} + +static void log_progress(struct encryptGroupsData const* data, bool completed) { + // Precondition - if completed data = 0 else data != 0 + + // Track progress so we can skip logging blocks + static off64_t offset = -1; + + // Need to close existing 'Encrypting from' log? + if (completed || (offset != -1 && data->offset != offset)) { + LOG(INFO) << "Encrypted to sector " << offset / info.block_size * CRYPT_SECTOR_SIZE; + offset = -1; + } + + // Need to start new 'Encrypting from' log? + if (!completed && offset != data->offset) { + LOG(INFO) << "Encrypting from sector " << data->offset / info.block_size * CRYPT_SECTOR_SIZE; + } + + // Update offset + if (!completed) { + offset = data->offset + (off64_t)data->count * info.block_size; + } +} + +static int flush_outstanding_data(struct encryptGroupsData* data) { + if (data->count == 0) { + return 0; + } + + LOG(DEBUG) << "Copying " << data->count << " blocks at offset " << data->offset; + + if (pread64(data->realfd, data->buffer, info.block_size * data->count, data->offset) <= 0) { + LOG(ERROR) << "Error reading real_blkdev " << data->real_blkdev << " for inplace encrypt"; + return -1; + } + + if (pwrite64(data->cryptofd, data->buffer, info.block_size * data->count, data->offset) <= 0) { + LOG(ERROR) << "Error writing crypto_blkdev " << data->crypto_blkdev + << " for inplace encrypt"; + return -1; + } else { + log_progress(data, false); + } + + data->count = 0; + data->last_written_sector = + (data->offset + data->count) / info.block_size * CRYPT_SECTOR_SIZE - 1; + return 0; +} + +static int encrypt_groups(struct encryptGroupsData* data) { + unsigned int i; + u8* block_bitmap = 0; + unsigned int block; + off64_t ret; + int rc = -1; + + data->buffer = (char*)malloc(info.block_size * BLOCKS_AT_A_TIME); + if (!data->buffer) { + LOG(ERROR) << "Failed to allocate crypto buffer"; + goto errout; + } + + block_bitmap = (u8*)malloc(info.block_size); + if (!block_bitmap) { + LOG(ERROR) << "failed to allocate block bitmap"; + goto errout; + } + + for (i = 0; i < aux_info.groups; ++i) { + LOG(INFO) << "Encrypting group " << i; + + u32 first_block = aux_info.first_data_block + i * info.blocks_per_group; + u32 block_count = std::min(info.blocks_per_group, (u32)(aux_info.len_blocks - first_block)); + + off64_t offset = (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap; + + ret = pread64(data->realfd, block_bitmap, info.block_size, offset); + if (ret != (int)info.block_size) { + LOG(ERROR) << "failed to read all of block group bitmap " << i; + goto errout; + } + + offset = (u64)info.block_size * first_block; + + data->count = 0; + + for (block = 0; block < block_count; block++) { + int used = (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT) + ? 0 + : bitmap_get_bit(block_bitmap, block); + update_progress(data, used); + if (used) { + if (data->count == 0) { + data->offset = offset; + } + data->count++; + } else { + if (flush_outstanding_data(data)) { + goto errout; + } + } + + offset += info.block_size; + + /* Write data if we are aligned or buffer size reached */ + if (offset % (info.block_size * BLOCKS_AT_A_TIME) == 0 || + data->count == BLOCKS_AT_A_TIME) { + if (flush_outstanding_data(data)) { + goto errout; + } + } + } + if (flush_outstanding_data(data)) { + goto errout; + } + } + + data->completed = 1; + rc = 0; + +errout: + log_progress(0, true); + free(data->buffer); + free(block_bitmap); + return rc; +} + +static int cryptfs_enable_inplace_ext4(const char* crypto_blkdev, const char* real_blkdev, + off64_t size, off64_t* size_already_done, off64_t tot_size, + off64_t previously_encrypted_upto, + bool set_progress_properties) { + u32 i; + struct encryptGroupsData data; + int rc; // Can't initialize without causing warning -Wclobbered + int retries = RETRY_MOUNT_ATTEMPTS; + struct timespec time_started = {0}; + + if (previously_encrypted_upto > *size_already_done) { + LOG(DEBUG) << "Not fast encrypting since resuming part way through"; + return -1; + } + + memset(&data, 0, sizeof(data)); + data.real_blkdev = real_blkdev; + data.crypto_blkdev = crypto_blkdev; + data.set_progress_properties = set_progress_properties; + + LOG(DEBUG) << "Opening" << real_blkdev; + if ((data.realfd = open(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) { + PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt"; + rc = -1; + goto errout; + } + + LOG(DEBUG) << "Opening" << crypto_blkdev; + // Wait until the block device appears. Re-use the mount retry values since it is reasonable. + while ((data.cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) { + if (--retries) { + PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev + << " for ext4 inplace encrypt, retrying"; + sleep(RETRY_MOUNT_DELAY_SECONDS); + } else { + PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev + << " for ext4 inplace encrypt"; + rc = ENABLE_INPLACE_ERR_DEV; + goto errout; + } + } + + if (setjmp(setjmp_env)) { // NOLINT + LOG(ERROR) << "Reading ext4 extent caused an exception"; + rc = -1; + goto errout; + } + + if (read_ext(data.realfd, 0) != 0) { + LOG(ERROR) << "Failed to read ext4 extent"; + rc = -1; + goto errout; + } + + data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE; + data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE; + data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE; + + LOG(INFO) << "Encrypting ext4 filesystem in place..."; + + data.tot_used_blocks = data.numblocks; + for (i = 0; i < aux_info.groups; ++i) { + data.tot_used_blocks -= aux_info.bg_desc[i].bg_free_blocks_count; + } + + data.one_pct = data.tot_used_blocks / 100; + data.cur_pct = 0; + + if (clock_gettime(CLOCK_MONOTONIC, &time_started)) { + LOG(WARNING) << "Error getting time at start"; + // Note - continue anyway - we'll run with 0 + } + data.time_started = time_started.tv_sec; + data.remaining_time = -1; + + rc = encrypt_groups(&data); + if (rc) { + LOG(ERROR) << "Error encrypting groups"; + goto errout; + } + + *size_already_done += data.completed ? size : data.last_written_sector; + rc = 0; + +errout: + close(data.realfd); + close(data.cryptofd); + + return rc; +} + +static void log_progress_f2fs(u64 block, bool completed) { + // Precondition - if completed data = 0 else data != 0 + + // Track progress so we can skip logging blocks + static u64 last_block = (u64)-1; + + // Need to close existing 'Encrypting from' log? + if (completed || (last_block != (u64)-1 && block != last_block + 1)) { + LOG(INFO) << "Encrypted to block " << last_block; + last_block = -1; + } + + // Need to start new 'Encrypting from' log? + if (!completed && (last_block == (u64)-1 || block != last_block + 1)) { + LOG(INFO) << "Encrypting from block " << block; + } + + // Update offset + if (!completed) { + last_block = block; + } +} + +static int encrypt_one_block_f2fs(u64 pos, void* data) { + struct encryptGroupsData* priv_dat = (struct encryptGroupsData*)data; + + priv_dat->blocks_already_done = pos - 1; + update_progress(priv_dat, 1); + + off64_t offset = pos * CRYPT_INPLACE_BUFSIZE; + + if (pread64(priv_dat->realfd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) { + LOG(ERROR) << "Error reading real_blkdev " << priv_dat->crypto_blkdev + << " for f2fs inplace encrypt"; + return -1; + } + + if (pwrite64(priv_dat->cryptofd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) { + LOG(ERROR) << "Error writing crypto_blkdev " << priv_dat->crypto_blkdev + << " for f2fs inplace encrypt"; + return -1; + } else { + log_progress_f2fs(pos, false); + } + + return 0; +} + +static int cryptfs_enable_inplace_f2fs(const char* crypto_blkdev, const char* real_blkdev, + off64_t size, off64_t* size_already_done, off64_t tot_size, + off64_t previously_encrypted_upto, + bool set_progress_properties) { + struct encryptGroupsData data; + struct f2fs_info* f2fs_info = NULL; + int rc = ENABLE_INPLACE_ERR_OTHER; + if (previously_encrypted_upto > *size_already_done) { + LOG(DEBUG) << "Not fast encrypting since resuming part way through"; + return ENABLE_INPLACE_ERR_OTHER; + } + memset(&data, 0, sizeof(data)); + data.real_blkdev = real_blkdev; + data.crypto_blkdev = crypto_blkdev; + data.set_progress_properties = set_progress_properties; + data.realfd = -1; + data.cryptofd = -1; + if ((data.realfd = open64(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) { + PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for f2fs inplace encrypt"; + goto errout; + } + if ((data.cryptofd = open64(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) { + PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev + << " for f2fs inplace encrypt"; + rc = ENABLE_INPLACE_ERR_DEV; + goto errout; + } + + f2fs_info = generate_f2fs_info(data.realfd); + if (!f2fs_info) goto errout; + + data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE; + data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE; + data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE; + + data.tot_used_blocks = get_num_blocks_used(f2fs_info); + + data.one_pct = data.tot_used_blocks / 100; + data.cur_pct = 0; + data.time_started = time(NULL); + data.remaining_time = -1; + + data.buffer = (char*)malloc(f2fs_info->block_size); + if (!data.buffer) { + LOG(ERROR) << "Failed to allocate crypto buffer"; + goto errout; + } + + data.count = 0; + + /* Currently, this either runs to completion, or hits a nonrecoverable error */ + rc = run_on_used_blocks(data.blocks_already_done, f2fs_info, &encrypt_one_block_f2fs, &data); + + if (rc) { + LOG(ERROR) << "Error in running over f2fs blocks"; + rc = ENABLE_INPLACE_ERR_OTHER; + goto errout; + } + + *size_already_done += size; + rc = 0; + +errout: + if (rc) LOG(ERROR) << "Failed to encrypt f2fs filesystem on " << real_blkdev; + + log_progress_f2fs(0, true); + free(f2fs_info); + free(data.buffer); + close(data.realfd); + close(data.cryptofd); + + return rc; +} + +static int cryptfs_enable_inplace_full(const char* crypto_blkdev, const char* real_blkdev, + off64_t size, off64_t* size_already_done, off64_t tot_size, + off64_t previously_encrypted_upto, + bool set_progress_properties) { + int realfd, cryptofd; + char* buf[CRYPT_INPLACE_BUFSIZE]; + int rc = ENABLE_INPLACE_ERR_OTHER; + off64_t numblocks, i, remainder; + off64_t one_pct, cur_pct, new_pct; + off64_t blocks_already_done, tot_numblocks; + + if ((realfd = open(real_blkdev, O_RDONLY | O_CLOEXEC)) < 0) { + PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt"; + return ENABLE_INPLACE_ERR_OTHER; + } + + if ((cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) { + PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt"; + close(realfd); + return ENABLE_INPLACE_ERR_DEV; + } + + /* This is pretty much a simple loop of reading 4K, and writing 4K. + * The size passed in is the number of 512 byte sectors in the filesystem. + * So compute the number of whole 4K blocks we should read/write, + * and the remainder. + */ + numblocks = size / CRYPT_SECTORS_PER_BUFSIZE; + remainder = size % CRYPT_SECTORS_PER_BUFSIZE; + tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE; + blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE; + + LOG(ERROR) << "Encrypting filesystem in place..."; + + i = previously_encrypted_upto + 1 - *size_already_done; + + if (lseek64(realfd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) { + PLOG(ERROR) << "Cannot seek to previously encrypted point on " << real_blkdev; + goto errout; + } + + if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) { + PLOG(ERROR) << "Cannot seek to previously encrypted point on " << crypto_blkdev; + goto errout; + } + + for (; i < size && i % CRYPT_SECTORS_PER_BUFSIZE != 0; ++i) { + if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) { + PLOG(ERROR) << "Error reading initial sectors from real_blkdev " << real_blkdev + << " for inplace encrypt"; + goto errout; + } + if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) { + PLOG(ERROR) << "Error writing initial sectors to crypto_blkdev " << crypto_blkdev + << " for inplace encrypt"; + goto errout; + } else { + LOG(INFO) << "Encrypted 1 block at " << i; + } + } + + one_pct = tot_numblocks / 100; + cur_pct = 0; + /* process the majority of the filesystem in blocks */ + for (i /= CRYPT_SECTORS_PER_BUFSIZE; i < numblocks; i++) { + new_pct = (i + blocks_already_done) / one_pct; + if (set_progress_properties && new_pct > cur_pct) { + char property_buf[8]; + + cur_pct = new_pct; + snprintf(property_buf, sizeof(property_buf), "%" PRId64, cur_pct); + android::base::SetProperty("vold.encrypt_progress", property_buf); + } + if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) { + PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev << " for inplace encrypt"; + goto errout; + } + if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) { + PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev << " for inplace encrypt"; + goto errout; + } else { + LOG(DEBUG) << "Encrypted " << CRYPT_SECTORS_PER_BUFSIZE << " block at " + << i * CRYPT_SECTORS_PER_BUFSIZE; + } + } + + /* Do any remaining sectors */ + for (i = 0; i < remainder; i++) { + if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) { + LOG(ERROR) << "Error reading final sectors from real_blkdev " << real_blkdev + << " for inplace encrypt"; + goto errout; + } + if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) { + LOG(ERROR) << "Error writing final sectors to crypto_blkdev " << crypto_blkdev + << " for inplace encrypt"; + goto errout; + } else { + LOG(INFO) << "Encrypted 1 block at next location"; + } + } + + *size_already_done += size; + rc = 0; + +errout: + close(realfd); + close(cryptofd); + + return rc; +} + +/* returns on of the ENABLE_INPLACE_* return codes */ +int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size, + off64_t* size_already_done, off64_t tot_size, + off64_t previously_encrypted_upto, bool set_progress_properties) { + int rc_ext4, rc_f2fs, rc_full; + LOG(DEBUG) << "cryptfs_enable_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << size + << ", " << size_already_done << ", " << tot_size << ", " << previously_encrypted_upto + << ", " << set_progress_properties << ")"; + if (previously_encrypted_upto) { + LOG(DEBUG) << "Continuing encryption from " << previously_encrypted_upto; + } + + if (*size_already_done + size < previously_encrypted_upto) { + LOG(DEBUG) << "cryptfs_enable_inplace already done"; + *size_already_done += size; + return 0; + } + + /* TODO: identify filesystem type. + * As is, cryptfs_enable_inplace_ext4 will fail on an f2fs partition, and + * then we will drop down to cryptfs_enable_inplace_f2fs. + * */ + if ((rc_ext4 = cryptfs_enable_inplace_ext4(crypto_blkdev, real_blkdev, size, size_already_done, + tot_size, previously_encrypted_upto, + set_progress_properties)) == 0) { + LOG(DEBUG) << "cryptfs_enable_inplace_ext4 success"; + return 0; + } + LOG(DEBUG) << "cryptfs_enable_inplace_ext4()=" << rc_ext4; + + if ((rc_f2fs = cryptfs_enable_inplace_f2fs(crypto_blkdev, real_blkdev, size, size_already_done, + tot_size, previously_encrypted_upto, + set_progress_properties)) == 0) { + LOG(DEBUG) << "cryptfs_enable_inplace_f2fs success"; + return 0; + } + LOG(DEBUG) << "cryptfs_enable_inplace_f2fs()=" << rc_f2fs; + + rc_full = + cryptfs_enable_inplace_full(crypto_blkdev, real_blkdev, size, size_already_done, tot_size, + previously_encrypted_upto, set_progress_properties); + LOG(DEBUG) << "cryptfs_enable_inplace_full()=" << rc_full; + + /* Hack for b/17898962, the following is the symptom... */ + if (rc_ext4 == ENABLE_INPLACE_ERR_DEV && rc_f2fs == ENABLE_INPLACE_ERR_DEV && + rc_full == ENABLE_INPLACE_ERR_DEV) { + LOG(DEBUG) << "ENABLE_INPLACE_ERR_DEV"; + return ENABLE_INPLACE_ERR_DEV; + } + return rc_full; +} diff --git a/crypto/fscrypt/EncryptInplace.h b/crypto/fscrypt/EncryptInplace.h new file mode 100644 index 00000000..bf0c3140 --- /dev/null +++ b/crypto/fscrypt/EncryptInplace.h @@ -0,0 +1,31 @@ +/* + * 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 _ENCRYPT_INPLACE_H +#define _ENCRYPT_INPLACE_H + +#include + +#define CRYPT_INPLACE_BUFSIZE 4096 +#define CRYPT_SECTOR_SIZE 512 +#define RETRY_MOUNT_ATTEMPTS 10 +#define RETRY_MOUNT_DELAY_SECONDS 1 + +int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size, + off64_t* size_already_done, off64_t tot_size, + off64_t previously_encrypted_upto, bool set_progress_properties); + +#endif diff --git a/crypto/fscrypt/FsCrypt.cpp b/crypto/fscrypt/FsCrypt.cpp new file mode 100755 index 00000000..f1c88099 --- /dev/null +++ b/crypto/fscrypt/FsCrypt.cpp @@ -0,0 +1,862 @@ +/* + * 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.h" + +#include "KeyStorage.h" +#include "KeyUtil.h" +#include "Utils.h" +// #include "VoldUtil.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// #include "android/os/IVold.h" + +#include "cryptfs.h" + +#define EMULATED_USES_SELINUX 0 +#define MANAGE_MISC_DIRS 0 + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +using android::base::StringPrintf; +using android::fs_mgr::GetEntryForMountPoint; +using android::vold::kEmptyAuthentication; +using android::vold::KeyBuffer; +// using android::vold::writeStringToFile; + +// Store main DE raw ref / policy +std::string de_raw_ref; +std::map s_de_key_raw_refs; +std::map s_ce_key_raw_refs; + +namespace { + +struct PolicyKeyRef { + std::string contents_mode; + std::string filenames_mode; + std::string key_raw_ref; +}; + +const std::string device_key_dir = std::string() + DATA_MNT_POINT + fscrypt_unencrypted_folder; +const std::string device_key_path = device_key_dir + "/key"; +const std::string device_key_temp = device_key_dir + "/temp"; + +const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys"; +const std::string user_key_temp = user_key_dir + "/temp"; +const std::string prepare_subdirs_path = "/sbin/vold_prepare_subdirs"; + +const std::string systemwide_volume_key_dir = + std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys"; +const int STORAGE_FLAG_DE = 1; +const int STORAGE_FLAG_CE = 2; + +bool s_systemwide_keys_initialized = false; + +android::fs_mgr::Fstab fstab_default; + +// Some users are ephemeral, don't try to wipe their keys from disk +std::set s_ephemeral_users; + +// TODO abolish this map, per b/26948053 +std::map s_ce_keys; + +} // namespace + +static bool fscrypt_is_emulated() { + return property_get_bool("persist.sys.emulate_fbe", false); +} + +static const char* escape_empty(const std::string& value) { + return value.empty() ? "null" : value.c_str(); +} + +static std::string get_de_key_path(userid_t user_id) { + return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id); +} + +static std::string get_ce_key_directory_path(userid_t user_id) { + return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id); +} + +// Returns the keys newest first +static std::vector get_ce_key_paths(const std::string& directory_path) { + auto dirp = std::unique_ptr(opendir(directory_path.c_str()), closedir); + if (!dirp) { + PLOG(ERROR) << "Unable to open ce key directory: " + directory_path; + return std::vector(); + } + std::vector result; + for (;;) { + errno = 0; + auto const entry = readdir(dirp.get()); + if (!entry) { + if (errno) { + PLOG(ERROR) << "Unable to read ce key directory: " + directory_path; + return std::vector(); + } + break; + } + if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') { + LOG(DEBUG) << "Skipping non-key " << entry->d_name; + continue; + } + result.emplace_back(directory_path + "/" + entry->d_name); + } + std::sort(result.begin(), result.end()); + std::reverse(result.begin(), result.end()); + return result; +} + +static std::string get_ce_key_current_path(const std::string& directory_path) { + return directory_path + "/current"; +} + +static bool get_ce_key_new_path(const std::string& directory_path, + const std::vector& paths, std::string* ce_key_path) { + if (paths.empty()) { + *ce_key_path = get_ce_key_current_path(directory_path); + return true; + } + for (unsigned int i = 0; i < UINT_MAX; i++) { + auto const candidate = StringPrintf("%s/cx%010u", directory_path.c_str(), i); + if (paths[0] < candidate) { + *ce_key_path = candidate; + return true; + } + } + return false; +} + +// Discard all keys but the named one; rename it to canonical name. +// No point in acting on errors in this; ignore them. +static void fixate_user_ce_key(const std::string& directory_path, const std::string& to_fix, + const std::vector& paths) { + for (auto const other_path : paths) { + if (other_path != to_fix) { + android::vold::destroyKey(other_path); + } + } + auto const current_path = get_ce_key_current_path(directory_path); + if (to_fix != current_path) { + LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path; + if (rename(to_fix.c_str(), current_path.c_str()) != 0) { + PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path; + return; + } + } + android::vold::FsyncDirectory(directory_path); +} + +static bool read_and_fixate_user_ce_key(userid_t user_id, + const android::vold::KeyAuthentication& auth, + KeyBuffer* ce_key) { + auto const directory_path = get_ce_key_directory_path(user_id); + auto const paths = get_ce_key_paths(directory_path); + for (auto const ce_key_path : paths) { + LOG(DEBUG) << "Trying user CE key " << ce_key_path; + if (android::vold::retrieveKey(ce_key_path, auth, ce_key)) { + LOG(DEBUG) << "Successfully retrieved key"; + fixate_user_ce_key(directory_path, ce_key_path, paths); + return true; + } + } + LOG(ERROR) << "Failed to find working ce key for user " << user_id; + return false; +} + +static bool read_and_install_user_ce_key(userid_t user_id, + const android::vold::KeyAuthentication& auth) { + if (s_ce_key_raw_refs.count(user_id) != 0) return true; + KeyBuffer ce_key; + if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false; + std::string ce_raw_ref; + if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false; + s_ce_keys[user_id] = std::move(ce_key); + s_ce_key_raw_refs[user_id] = ce_raw_ref; + LOG(DEBUG) << "Installed ce key for user " << user_id; + return true; +} + +static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) { + LOG(DEBUG) << "Preparing: " << dir; + if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) { + PLOG(ERROR) << "Failed to prepare " << dir; + return false; + } + return true; +} + +static bool destroy_dir(const std::string& dir) { + LOG(DEBUG) << "Destroying: " << dir; + if (rmdir(dir.c_str()) != 0 && errno != ENOENT) { + PLOG(ERROR) << "Failed to destroy " << dir; + return false; + } + return true; +} + +// NB this assumes that there is only one thread listening for crypt commands, because +// it creates keys in a fixed location. +static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) { + KeyBuffer de_key, ce_key; + if (!android::vold::randomKey(&de_key)) return false; + if (!android::vold::randomKey(&ce_key)) return false; + if (create_ephemeral) { + // If the key should be created as ephemeral, don't store it. + s_ephemeral_users.insert(user_id); + } else { + auto const directory_path = get_ce_key_directory_path(user_id); + if (!prepare_dir(directory_path, 0700, AID_ROOT, AID_ROOT)) return false; + auto const paths = get_ce_key_paths(directory_path); + std::string ce_key_path; + if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false; + if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, kEmptyAuthentication, + ce_key)) + return false; + fixate_user_ce_key(directory_path, ce_key_path, paths); + // Write DE key second; once this is written, all is good. + if (!android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp, + kEmptyAuthentication, de_key)) + return false; + } + if (!android::vold::installKey(de_key, &de_raw_ref)) return false; + s_de_key_raw_refs[user_id] = de_raw_ref; + std::string ce_raw_ref; + if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false; + s_ce_keys[user_id] = ce_key; + s_ce_key_raw_refs[user_id] = ce_raw_ref; + LOG(DEBUG) << "Created keys for user " << user_id; + return true; +} + +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()) { + LOG(DEBUG) << "Cannot find key for " << user_id; + return false; + } + *raw_ref = refi->second; + return true; +} + +static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) { + if (!ReadDefaultFstab(&fstab_default)) { + PLOG(ERROR) << "Failed to open default fstab"; + return; + } + auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT); + if (entry == nullptr) { + LOG(ERROR) << "get_data_file_encryption_modes::failed\n"; + return; + } + key_ref->contents_mode = entry->file_contents_mode; + key_ref->filenames_mode = entry->file_names_mode; +} + +static bool ensure_policy(const PolicyKeyRef& key_ref, const std::string& path) { + return fscrypt_policy_ensure(path.c_str(), key_ref.key_raw_ref.data(), + key_ref.key_raw_ref.size(), key_ref.contents_mode.c_str(), + key_ref.filenames_mode.c_str()) == 0; +} + +static bool is_numeric(const char* name) { + for (const char* p = name; *p != '\0'; p++) { + if (!isdigit(*p)) return false; + } + return true; +} + +static bool load_all_de_keys() { + auto de_dir = user_key_dir + "/de"; + auto dirp = std::unique_ptr(opendir(de_dir.c_str()), closedir); + if (!dirp) { + PLOG(ERROR) << "Unable to read de key directory"; + return false; + } + for (;;) { + errno = 0; + auto entry = readdir(dirp.get()); + if (!entry) { + if (errno) { + PLOG(ERROR) << "Unable to read de key directory"; + return false; + } + break; + } + if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) { + LOG(DEBUG) << "Skipping non-de-key " << entry->d_name; + continue; + } + userid_t user_id = std::stoi(entry->d_name); + if (s_de_key_raw_refs.count(user_id) == 0) { + auto key_path = de_dir + "/" + entry->d_name; + KeyBuffer key; + if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false; + std::string raw_ref; + if (!android::vold::installKey(key, &raw_ref)) return false; + s_de_key_raw_refs[user_id] = raw_ref; + LOG(DEBUG) << "Installed de key for user " << user_id; + } + } + // fscrypt:TODO: go through all DE directories, ensure that all user dirs have the + // correct policy set on them, and that no rogue ones exist. + return true; +} + +bool fscrypt_initialize_systemwide_keys() { + LOG(INFO) << "fscrypt_initialize_systemwide_keys"; + + if (s_systemwide_keys_initialized) { + LOG(INFO) << "Already initialized"; + return true; + } + + PolicyKeyRef device_ref; + if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path, + device_key_temp, &device_ref.key_raw_ref)) + return false; + get_data_file_encryption_modes(&device_ref); + + std::string modestring = device_ref.contents_mode + ":" + device_ref.filenames_mode; + std::string mode_filename = std::string("/data") + fscrypt_key_mode; + if (!android::vold::writeStringToFile(modestring, mode_filename)) return false; + + std::string ref_filename = std::string("/data") + fscrypt_key_ref; + if (!android::vold::writeStringToFile(device_ref.key_raw_ref, ref_filename)) return false; + LOG(INFO) << "Wrote system DE key reference to:" << ref_filename; + + KeyBuffer per_boot_key; + if (!android::vold::randomKey(&per_boot_key)) return false; + std::string per_boot_raw_ref; + if (!android::vold::installKey(per_boot_key, &per_boot_raw_ref)) return false; + std::string per_boot_ref_filename = std::string("/data") + fscrypt_key_per_boot_ref; + if (!android::vold::writeStringToFile(per_boot_raw_ref, per_boot_ref_filename)) return false; + LOG(INFO) << "Wrote per boot key reference to:" << per_boot_ref_filename; + + if (!android::vold::FsyncDirectory(device_key_dir)) return false; + s_systemwide_keys_initialized = true; + de_raw_ref = device_ref.key_raw_ref; + return true; +} + +bool fscrypt_init_user0() { + if (!ReadDefaultFstab(&fstab_default)) { + PLOG(ERROR) << "Failed to open default fstab"; + return -1; + } + if (fscrypt_is_native()) { + if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false; + if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false; + if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false; + if (!android::vold::pathExists(get_de_key_path(0))) { + if (!create_and_install_user_keys(0, false)) return false; + } + // TODO: switch to loading only DE_0 here once framework makes + // explicit calls to install DE keys for secondary users + if (!load_all_de_keys()) return false; + } + // We can only safely prepare DE storage here, since CE keys are probably + // entangled with user credentials. The framework will always prepare CE + // storage once CE keys are installed. + if (!fscrypt_prepare_user_storage("", 0, 0, STORAGE_FLAG_DE)) { + LOG(ERROR) << "Failed to prepare user 0 storage"; + return false; + } + // If this is a non-FBE device that recently left an emulated mode, + // restore user data directories to known-good state. + if (!fscrypt_is_native() && !fscrypt_is_emulated()) { + fscrypt_unlock_user_key(0, 0, "!", "!"); + } + + return true; +} + +bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) { + LOG(DEBUG) << "fscrypt_vold_create_user_key for " << user_id << " serial " << serial; + if (!fscrypt_is_native()) { + return true; + } + // FIXME test for existence of key that is not loaded yet + if (s_ce_key_raw_refs.count(user_id) != 0) { + LOG(ERROR) << "Already exists, can't fscrypt_vold_create_user_key for " << user_id + << " serial " << serial; + // FIXME should we fail the command? + return true; + } + if (!create_and_install_user_keys(user_id, ephemeral)) { + return false; + } + return true; +} + +static void drop_caches() { + // Clean any dirty pages (otherwise they won't be dropped). + sync(); + // Drop inode and page caches. + if (!android::vold::writeStringToFile("3", "/proc/sys/vm/drop_caches")) { + PLOG(ERROR) << "Failed to drop caches during key eviction"; + } +} + +static bool evict_ce_key(userid_t user_id) { + s_ce_keys.erase(user_id); + bool success = true; + std::string raw_ref; + // If we haven't loaded the CE key, no need to evict it. + if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) { + success &= android::vold::evictKey(raw_ref); + drop_caches(); + } + s_ce_key_raw_refs.erase(user_id); + return success; +} + +bool fscrypt_destroy_user_key(userid_t user_id) { + LOG(DEBUG) << "fscrypt_destroy_user_key(" << user_id << ")"; + if (!fscrypt_is_native()) { + return true; + } + bool success = true; + std::string raw_ref; + success &= evict_ce_key(user_id); + success &= + lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref) && android::vold::evictKey(raw_ref); + s_de_key_raw_refs.erase(user_id); + auto it = s_ephemeral_users.find(user_id); + if (it != s_ephemeral_users.end()) { + s_ephemeral_users.erase(it); + } else { + for (auto const path : get_ce_key_paths(get_ce_key_directory_path(user_id))) { + success &= android::vold::destroyKey(path); + } + auto de_key_path = get_de_key_path(user_id); + if (android::vold::pathExists(de_key_path)) { + success &= android::vold::destroyKey(de_key_path); + } else { + LOG(INFO) << "Not present so not erasing: " << de_key_path; + } + } + return success; +} + +static bool emulated_lock(const std::string& path) { + if (chmod(path.c_str(), 0000) != 0) { + PLOG(ERROR) << "Failed to chmod " << path; + return false; + } +#if EMULATED_USES_SELINUX + if (setfilecon(path.c_str(), "u:object_r:storage_stub_file:s0") != 0) { + PLOG(WARNING) << "Failed to setfilecon " << path; + return false; + } +#endif + return true; +} + +static bool emulated_unlock(const std::string& path, mode_t mode) { + if (chmod(path.c_str(), mode) != 0) { + PLOG(ERROR) << "Failed to chmod " << path; + // FIXME temporary workaround for b/26713622 + if (fscrypt_is_emulated()) return false; + } +#if EMULATED_USES_SELINUX + if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_FORCE) != 0) { + PLOG(WARNING) << "Failed to restorecon " << path; + // FIXME temporary workaround for b/26713622 + if (fscrypt_is_emulated()) return false; + } +#endif + return true; +} + +static bool parse_hex(const std::string& hex, std::string* result) { + if (hex == "!") { + *result = ""; + return true; + } + if (android::vold::HexToStr(hex, *result) != 0) { + LOG(ERROR) << "Invalid FBE hex string"; // Don't log the string for security reasons + return false; + } + return true; +} + +static std::string volkey_path(const std::string& misc_path, const std::string& volume_uuid) { + return misc_path + "/vold/volume_keys/" + volume_uuid + "/default"; +} + +static std::string volume_secdiscardable_path(const std::string& volume_uuid) { + return systemwide_volume_key_dir + "/" + volume_uuid + "/secdiscardable"; +} + +static bool read_or_create_volkey(const std::string& misc_path, const std::string& volume_uuid, + PolicyKeyRef* key_ref) { + auto secdiscardable_path = volume_secdiscardable_path(volume_uuid); + std::string secdiscardable_hash; + if (android::vold::pathExists(secdiscardable_path)) { + if (!android::vold::readSecdiscardable(secdiscardable_path, &secdiscardable_hash)) + return false; + } else { + if (fs_mkdirs(secdiscardable_path.c_str(), 0700) != 0) { + PLOG(ERROR) << "Creating directories for: " << secdiscardable_path; + return false; + } + if (!android::vold::createSecdiscardable(secdiscardable_path, &secdiscardable_hash)) + return false; + } + auto key_path = volkey_path(misc_path, volume_uuid); + if (fs_mkdirs(key_path.c_str(), 0700) != 0) { + PLOG(ERROR) << "Creating directories for: " << key_path; + return false; + } + android::vold::KeyAuthentication auth("", secdiscardable_hash); + if (!android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp", + &key_ref->key_raw_ref)) + return false; + key_ref->contents_mode = + android::base::GetProperty("ro.crypto.volume.contents_mode", "aes-256-xts"); + key_ref->filenames_mode = + android::base::GetProperty("ro.crypto.volume.filenames_mode", "aes-256-heh"); + return true; +} + +static bool destroy_volkey(const std::string& misc_path, const std::string& volume_uuid) { + auto path = volkey_path(misc_path, volume_uuid); + if (!android::vold::pathExists(path)) return true; + return android::vold::destroyKey(path); +} + +bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token_hex, + const std::string& secret_hex) { + LOG(DEBUG) << "fscrypt_add_user_key_auth " << user_id << " serial=" << serial + << " token_present=" << (token_hex != "!"); + if (!fscrypt_is_native()) return true; + if (s_ephemeral_users.count(user_id) != 0) return true; + std::string token, secret; + if (!parse_hex(token_hex, &token)) return false; + if (!parse_hex(secret_hex, &secret)) return false; + auto auth = + secret.empty() ? kEmptyAuthentication : android::vold::KeyAuthentication(token, secret); + auto it = s_ce_keys.find(user_id); + if (it == s_ce_keys.end()) { + LOG(ERROR) << "Key not loaded into memory, can't change for user " << user_id; + return false; + } + const auto& ce_key = it->second; + auto const directory_path = get_ce_key_directory_path(user_id); + auto const paths = get_ce_key_paths(directory_path); + std::string ce_key_path; + if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false; + if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, auth, ce_key)) return false; + if (!android::vold::FsyncDirectory(directory_path)) return false; + return true; +} + +bool fscrypt_fixate_newest_user_key_auth(userid_t user_id) { + LOG(DEBUG) << "fscrypt_fixate_newest_user_key_auth " << user_id; + if (!fscrypt_is_native()) return true; + if (s_ephemeral_users.count(user_id) != 0) return true; + auto const directory_path = get_ce_key_directory_path(user_id); + auto const paths = get_ce_key_paths(directory_path); + if (paths.empty()) { + LOG(ERROR) << "No ce keys present, cannot fixate for user " << user_id; + return false; + } + fixate_user_ce_key(directory_path, paths[0], paths); + return true; +} + +// TODO: rename to 'install' for consistency, and take flags to know which keys to install +bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex, + const std::string& secret_hex) { + LOG(DEBUG) << "fscrypt_unlock_user_key " << user_id << " serial=" << serial + << " token_present=" << (token_hex != "!"); + if (fscrypt_is_native()) { + if (s_ce_key_raw_refs.count(user_id) != 0) { + LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id; + return true; + } + std::string token, secret; + if (!parse_hex(token_hex, &token)) return false; + if (!parse_hex(secret_hex, &secret)) return false; + android::vold::KeyAuthentication auth(token, secret); + if (!read_and_install_user_ce_key(user_id, auth)) { + LOG(ERROR) << "Couldn't read key for " << user_id; + return false; + } + } else { + // When in emulation mode, we just use chmod. However, we also + // unlock directories when not in emulation mode, to bring devices + // back into a known-good state. + if (!emulated_unlock(android::vold::BuildDataSystemCePath(user_id), 0771) || + !emulated_unlock(android::vold::BuildDataMiscCePath(user_id), 01771) || + !emulated_unlock(android::vold::BuildDataMediaCePath("", user_id), 0770) || + !emulated_unlock(android::vold::BuildDataUserCePath("", user_id), 0771)) { + LOG(ERROR) << "Failed to unlock user " << user_id; + return false; + } + } + return true; +} + +// TODO: rename to 'evict' for consistency +bool fscrypt_lock_user_key(userid_t user_id) { + LOG(DEBUG) << "fscrypt_lock_user_key " << user_id; + if (fscrypt_is_native()) { + return evict_ce_key(user_id); + } else if (fscrypt_is_emulated()) { + // When in emulation mode, we just use chmod + if (!emulated_lock(android::vold::BuildDataSystemCePath(user_id)) || + !emulated_lock(android::vold::BuildDataMiscCePath(user_id)) || + !emulated_lock(android::vold::BuildDataMediaCePath("", user_id)) || + !emulated_lock(android::vold::BuildDataUserCePath("", user_id))) { + LOG(ERROR) << "Failed to lock user " << user_id; + return false; + } + } + + return true; +} + +static bool prepare_subdirs(const std::string& action, const std::string& volume_uuid, + userid_t user_id, int flags) { + if (0 != android::vold::ForkExecvp( + std::vector{prepare_subdirs_path, action, volume_uuid, + std::to_string(user_id), std::to_string(flags)})) { + LOG(ERROR) << "vold_prepare_subdirs failed"; + return false; + } + return true; +} + +bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial, + int flags) { + LOG(DEBUG) << "fscrypt_prepare_user_storage for volume " << escape_empty(volume_uuid) + << ", user " << user_id << ", serial " << serial << ", flags " << flags; + + if (flags & STORAGE_FLAG_DE) { + // DE_sys key + auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id); + auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id); + auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id); + + // DE_n key + auto system_de_path = android::vold::BuildDataSystemDePath(user_id); + auto misc_de_path = android::vold::BuildDataMiscDePath(user_id); + auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id); + auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id); + + if (volume_uuid.empty()) { + if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false; +#if MANAGE_MISC_DIRS + if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM), + multiuser_get_uid(user_id, AID_EVERYBODY))) + return false; +#endif + if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; + + if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false; + if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false; + if (!prepare_dir(vendor_de_path, 0771, AID_ROOT, AID_ROOT)) return false; + } + if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; + + if (fscrypt_is_native()) { + PolicyKeyRef de_ref; + if (volume_uuid.empty()) { + if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_ref.key_raw_ref)) return false; + get_data_file_encryption_modes(&de_ref); + if (!ensure_policy(de_ref, system_de_path)) return false; + if (!ensure_policy(de_ref, misc_de_path)) return false; + if (!ensure_policy(de_ref, vendor_de_path)) return false; + } else { + if (!read_or_create_volkey(misc_de_path, volume_uuid, &de_ref)) return false; + } + if (!ensure_policy(de_ref, user_de_path)) return false; + } + } + if (flags & STORAGE_FLAG_CE) { + // CE_n key + auto system_ce_path = android::vold::BuildDataSystemCePath(user_id); + auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id); + auto vendor_ce_path = android::vold::BuildDataVendorCePath(user_id); + auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id); + auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id); + + if (volume_uuid.empty()) { + if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false; + if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false; + if (!prepare_dir(vendor_ce_path, 0771, AID_ROOT, AID_ROOT)) return false; + } + if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false; + if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; + + if (fscrypt_is_native()) { + PolicyKeyRef ce_ref; + if (volume_uuid.empty()) { + if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_ref.key_raw_ref)) return false; + get_data_file_encryption_modes(&ce_ref); + if (!ensure_policy(ce_ref, system_ce_path)) return false; + if (!ensure_policy(ce_ref, misc_ce_path)) return false; + if (!ensure_policy(ce_ref, vendor_ce_path)) return false; + + } else { + if (!read_or_create_volkey(misc_ce_path, volume_uuid, &ce_ref)) return false; + } + if (!ensure_policy(ce_ref, media_ce_path)) return false; + if (!ensure_policy(ce_ref, user_ce_path)) return false; + } + + if (volume_uuid.empty()) { + // Now that credentials have been installed, we can run restorecon + // over these paths + // NOTE: these paths need to be kept in sync with libselinux + android::vold::RestoreconRecursive(system_ce_path); + android::vold::RestoreconRecursive(vendor_ce_path); + android::vold::RestoreconRecursive(misc_ce_path); + } + } + if (!prepare_subdirs("prepare", volume_uuid, user_id, flags)) return false; + + return true; +} + +bool fscrypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags) { + LOG(DEBUG) << "fscrypt_destroy_user_storage for volume " << escape_empty(volume_uuid) + << ", user " << user_id << ", flags " << flags; + bool res = true; + + res &= prepare_subdirs("destroy", volume_uuid, user_id, flags); + + if (flags & STORAGE_FLAG_CE) { + // CE_n key + auto system_ce_path = android::vold::BuildDataSystemCePath(user_id); + auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id); + auto vendor_ce_path = android::vold::BuildDataVendorCePath(user_id); + auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id); + auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id); + + res &= destroy_dir(media_ce_path); + res &= destroy_dir(user_ce_path); + if (volume_uuid.empty()) { + res &= destroy_dir(system_ce_path); + res &= destroy_dir(misc_ce_path); + res &= destroy_dir(vendor_ce_path); + } else { + if (fscrypt_is_native()) { + res &= destroy_volkey(misc_ce_path, volume_uuid); + } + } + } + + if (flags & STORAGE_FLAG_DE) { + // DE_sys key + auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id); + auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id); + auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id); + + // DE_n key + auto system_de_path = android::vold::BuildDataSystemDePath(user_id); + auto misc_de_path = android::vold::BuildDataMiscDePath(user_id); + auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id); + auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id); + + res &= destroy_dir(user_de_path); + if (volume_uuid.empty()) { + res &= destroy_dir(system_legacy_path); +#if MANAGE_MISC_DIRS + res &= destroy_dir(misc_legacy_path); +#endif + res &= destroy_dir(profiles_de_path); + res &= destroy_dir(system_de_path); + res &= destroy_dir(misc_de_path); + res &= destroy_dir(vendor_de_path); + } else { + if (fscrypt_is_native()) { + res &= destroy_volkey(misc_de_path, volume_uuid); + } + } + } + + return res; +} + +static bool destroy_volume_keys(const std::string& directory_path, const std::string& volume_uuid) { + auto dirp = std::unique_ptr(opendir(directory_path.c_str()), closedir); + if (!dirp) { + PLOG(ERROR) << "Unable to open directory: " + directory_path; + return false; + } + bool res = true; + for (;;) { + errno = 0; + auto const entry = readdir(dirp.get()); + if (!entry) { + if (errno) { + PLOG(ERROR) << "Unable to read directory: " + directory_path; + return false; + } + break; + } + if (entry->d_type != DT_DIR || entry->d_name[0] == '.') { + LOG(DEBUG) << "Skipping non-user " << entry->d_name; + continue; + } + res &= destroy_volkey(directory_path + "/" + entry->d_name, volume_uuid); + } + return res; +} + +bool fscrypt_destroy_volume_keys(const std::string& volume_uuid) { + bool res = true; + LOG(DEBUG) << "fscrypt_destroy_volume_keys for volume " << escape_empty(volume_uuid); + auto secdiscardable_path = volume_secdiscardable_path(volume_uuid); + res &= android::vold::runSecdiscardSingle(secdiscardable_path); + res &= destroy_volume_keys("/data/misc_ce", volume_uuid); + res &= destroy_volume_keys("/data/misc_de", volume_uuid); + return res; +} diff --git a/crypto/fscrypt/FsCrypt.h b/crypto/fscrypt/FsCrypt.h new file mode 100755 index 00000000..ff32bc87 --- /dev/null +++ b/crypto/fscrypt/FsCrypt.h @@ -0,0 +1,43 @@ +/* + * 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 +#include +#include + +#include + +bool fscrypt_initialize_systemwide_keys(); + +bool fscrypt_init_user0(); +bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral); +bool fscrypt_destroy_user_key(userid_t user_id); +bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token, + const std::string& secret); +bool fscrypt_fixate_newest_user_key_auth(userid_t user_id); + +bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token, + const std::string& secret); +bool fscrypt_lock_user_key(userid_t user_id); + +bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial, + int flags); +bool fscrypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags); + +bool fscrypt_destroy_volume_keys(const std::string& volume_uuid); + +bool lookup_key_ref(const std::map& key_map, userid_t user_id, + std::string* raw_ref); \ No newline at end of file diff --git a/crypto/fscrypt/HashPassword.cpp b/crypto/fscrypt/HashPassword.cpp new file mode 100644 index 00000000..07ecb1f7 --- /dev/null +++ b/crypto/fscrypt/HashPassword.cpp @@ -0,0 +1,117 @@ +/* + * 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. + */ + +/* + * This computes the "secret" used by Android as one of the parameters + * to decrypt File Based Encryption. The secret is prefixed with + * "Android FBE credential hash" padded with 0s to 128 bytes then the + * user's password is appended to the end of the 128 bytes. This string + * is then hashed with sha512 and the sha512 value is then converted to + * hex with upper-case characters. + */ + +#include +#include +#include +#include +#include + +#include "HashPassword.h" + +#define PASS_PADDING_SIZE 128 +#define SHA512_HEX_SIZE SHA512_DIGEST_LENGTH * 2 +#define SHA256_HEX_SIZE SHA256_DIGEST_LENGTH * 2 + +void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size) { + size_t size = PASS_PADDING_SIZE + key_size; + unsigned char* buffer = (unsigned char*)calloc(1, size); + if (!buffer) return NULL; // failed to malloc + memcpy((void*)buffer, (void*)prefix, strlen(prefix)); + unsigned char* ptr = buffer + PASS_PADDING_SIZE; + memcpy((void*)ptr, key, key_size); + unsigned char hash[SHA512_DIGEST_LENGTH]; + SHA512_CTX sha512; + SHA512_Init(&sha512); + SHA512_Update(&sha512, buffer, size); + SHA512_Final(hash, &sha512); + free(buffer); + void* ret = malloc(SHA512_DIGEST_LENGTH); + if (!ret) return NULL; // failed to malloc + memcpy(ret, (void*)&hash[0], SHA512_DIGEST_LENGTH); + return ret; +} + +std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size) { + size_t size = PASS_PADDING_SIZE + key_size; + unsigned char* buffer = (unsigned char*)calloc(1, size); + if (!buffer) return ""; // failed to malloc + memcpy((void*)buffer, (void*)prefix, strlen(prefix)); + unsigned char* ptr = buffer + PASS_PADDING_SIZE; + memcpy((void*)ptr, key, key_size); + unsigned char hash[SHA512_DIGEST_LENGTH]; + SHA512_CTX sha512; + SHA512_Init(&sha512); + SHA512_Update(&sha512, buffer, size); + SHA512_Final(hash, &sha512); + int index = 0; + char hex_hash[SHA512_HEX_SIZE + 1]; + for(index = 0; index < SHA512_DIGEST_LENGTH; index++) + sprintf(hex_hash + (index * 2), "%02X", hash[index]); + hex_hash[128] = 0; + std::string ret = hex_hash; + free(buffer); + return ret; +} + +std::string PersonalizedHash(const char* prefix, const std::string& Password) { + return PersonalizedHash(prefix, Password.c_str(), Password.size()); +} + +std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size) { + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL); + unsigned int counter = 1; + endianswap(&counter); + HMAC_Update(&ctx, (const unsigned char*)&counter, 4); + HMAC_Update(&ctx, (const unsigned char*)label, strlen(label)); + const unsigned char divider = 0; + HMAC_Update(&ctx, ÷r, 1); + HMAC_Update(&ctx, (const unsigned char*)context, strlen(context)); + unsigned int contextDisambiguation = strlen(context) * 8; + endianswap(&contextDisambiguation); + HMAC_Update(&ctx, (const unsigned char*)&contextDisambiguation, 4); + unsigned int finalValue = 256; + endianswap(&finalValue); + HMAC_Update(&ctx, (const unsigned char*)&finalValue, 4); + + unsigned char output[SHA256_DIGEST_LENGTH]; + unsigned int out_size = 0; + HMAC_Final(&ctx, output, &out_size); + + int index = 0; + char hex_hash[SHA256_HEX_SIZE + 1]; + for(index = 0; index < SHA256_DIGEST_LENGTH; index++) + sprintf(hex_hash + (index * 2), "%02x", output[index]); + hex_hash[SHA256_HEX_SIZE] = 0; + std::string ret = hex_hash; + return ret; +} + +std::string HashPassword(const std::string& Password) { + const char* prefix = FBE_PERSONALIZATION; + return PersonalizedHash(prefix, Password); +} diff --git a/crypto/fscrypt/HashPassword.h b/crypto/fscrypt/HashPassword.h new file mode 100644 index 00000000..73880b1b --- /dev/null +++ b/crypto/fscrypt/HashPassword.h @@ -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. + */ + +#ifndef __HASH_PASSWORD_H +#define __HASH_PASSWORD_H + +#include + +#define FBE_PERSONALIZATION "Android FBE credential hash" +#define PERSONALISATION_WEAVER_KEY "weaver-key" +#define PERSONALISATION_WEAVER_PASSWORD "weaver-pwd" +#define PERSONALISATION_APPLICATION_ID "application-id" +#define PERSONALIZATION_FBE_KEY "fbe-key" +#define PERSONALIZATION_USER_GK_AUTH "user-gk-authentication" +#define PERSONALISATION_SECDISCARDABLE "secdiscardable-transform" +#define PERSONALISATION_CONTEXT "android-synthetic-password-personalization-context" + +void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size); + +std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size); +std::string PersonalizedHash(const char* prefix, const std::string& Password); +std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size); +std::string HashPassword(const std::string& Password); + +template +void endianswap(T *objp) { + unsigned char *memp = reinterpret_cast(objp); + std::reverse(memp, memp + sizeof(T)); +} + +#endif diff --git a/crypto/fscrypt/KeyBuffer.cpp b/crypto/fscrypt/KeyBuffer.cpp new file mode 100644 index 00000000..e7aede53 --- /dev/null +++ b/crypto/fscrypt/KeyBuffer.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 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 "KeyBuffer.h" + +#include +#include + +namespace android { +namespace vold { + +KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs) { + std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs)); + return std::move(lhs); +} + +KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs) { + std::copy(rhs, rhs + strlen(rhs), std::back_inserter(lhs)); + return std::move(lhs); +} + +} // namespace vold +} // namespace android + diff --git a/crypto/fscrypt/KeyBuffer.h b/crypto/fscrypt/KeyBuffer.h new file mode 100644 index 00000000..2087187d --- /dev/null +++ b/crypto/fscrypt/KeyBuffer.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 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 ANDROID_VOLD_KEYBUFFER_H +#define ANDROID_VOLD_KEYBUFFER_H + +#include +#include +#include + +namespace android { +namespace vold { + +/** + * Variant of memset() that should never be optimized away. Borrowed from keymaster code. + */ +#ifdef __clang__ +#define OPTNONE __attribute__((optnone)) +#else // not __clang__ +#define OPTNONE __attribute__((optimize("O0"))) +#endif // not __clang__ +inline OPTNONE void* memset_s(void* s, int c, size_t n) { + if (!s) + return s; + return memset(s, c, n); +} +#undef OPTNONE + +// Allocator that delegates useful work to standard one but zeroes data before deallocating. +class ZeroingAllocator : public std::allocator { + public: + void deallocate(pointer p, size_type n) + { + memset_s(p, 0, n); + std::allocator::deallocate(p, n); + } +}; + +// Char vector that zeroes memory when deallocating. +using KeyBuffer = std::vector; + +// Convenience methods to concatenate key buffers. +KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs); +KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs); + +} // namespace vold +} // namespace android + +#endif + diff --git a/crypto/fscrypt/KeyStorage.cpp b/crypto/fscrypt/KeyStorage.cpp new file mode 100755 index 00000000..c68daa1d --- /dev/null +++ b/crypto/fscrypt/KeyStorage.cpp @@ -0,0 +1,598 @@ +/* + * 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 "KeyStorage.h" + +#include "Keymaster.h" +#include "ScryptParameters.h" +#include "Utils.h" +#include "Checkpoint.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +extern "C" { + +#include "crypto_scrypt.h" +} + +namespace android { +namespace vold { + +const KeyAuthentication kEmptyAuthentication{"", ""}; + +static constexpr size_t AES_KEY_BYTES = 32; +static constexpr size_t GCM_NONCE_BYTES = 12; +static constexpr size_t GCM_MAC_BYTES = 16; +static constexpr size_t SALT_BYTES = 1 << 4; +static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14; +static constexpr size_t STRETCHED_BYTES = 1 << 6; + +static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds + +static const char* kCurrentVersion = "1"; +static const char* kRmPath = "/system/bin/rm"; +static const char* kSecdiscardPath = "/system/bin/secdiscard"; +static const char* kStretch_none = "none"; +static const char* kStretch_nopassword = "nopassword"; +static const std::string kStretchPrefix_scrypt = "scrypt "; +static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512"; +static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512"; +static const char* kFn_encrypted_key = "encrypted_key"; +static const char* kFn_keymaster_key_blob = "keymaster_key_blob"; +static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded"; +static const char* kFn_salt = "salt"; +static const char* kFn_secdiscardable = "secdiscardable"; +static const char* kFn_stretching = "stretching"; +static const char* kFn_version = "version"; + +static bool checkSize(const std::string& kind, size_t actual, size_t expected) { + if (actual != expected) { + LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got " + << actual; + return false; + } + return true; +} + +static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) { + SHA512_CTX c; + + SHA512_Init(&c); + // Personalise the hashing by introducing a fixed prefix. + // Hashing applications should use personalization except when there is a + // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf + std::string hashingPrefix = prefix; + hashingPrefix.resize(SHA512_CBLOCK); + SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size()); + SHA512_Update(&c, tohash.data(), tohash.size()); + res->assign(SHA512_DIGEST_LENGTH, '\0'); + SHA512_Final(reinterpret_cast(&(*res)[0]), &c); +} + +static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth, + const std::string& appId, std::string* key) { + auto paramBuilder = km::AuthorizationSetBuilder() + .AesEncryptionKey(AES_KEY_BYTES * 8) + .GcmModeMinMacLen(GCM_MAC_BYTES * 8) + .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId)); + if (auth.token.empty()) { + LOG(DEBUG) << "Creating key that doesn't need auth token"; + paramBuilder.Authorization(km::TAG_NO_AUTH_REQUIRED); + } else { + LOG(DEBUG) << "Auth token required for key"; + if (auth.token.size() != sizeof(hw_auth_token_t)) { + LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was " + << auth.token.size() << " bytes"; + return false; + } + const hw_auth_token_t* at = reinterpret_cast(auth.token.data()); + paramBuilder.Authorization(km::TAG_USER_SECURE_ID, at->user_id); + paramBuilder.Authorization(km::TAG_USER_AUTH_TYPE, km::HardwareAuthenticatorType::PASSWORD); + paramBuilder.Authorization(km::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT); + } + return keymaster.generateKey(paramBuilder, key); +} + +static std::pair beginParams( + const KeyAuthentication& auth, const std::string& appId) { + auto paramBuilder = km::AuthorizationSetBuilder() + .GcmModeMacLen(GCM_MAC_BYTES * 8) + .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId)); + km::HardwareAuthToken authToken; + if (!auth.token.empty()) { + LOG(DEBUG) << "Supplying auth token to Keymaster"; + authToken = km::support::hidlVec2AuthToken(km::support::blob2hidlVec(auth.token)); + } + return {paramBuilder, authToken}; +} + +static bool readFileToString(const std::string& filename, std::string* result) { + if (!android::base::ReadFileToString(filename, result)) { + PLOG(ERROR) << "Failed to read from " << filename; + return false; + } + return true; +} + +static bool readRandomBytesOrLog(size_t count, std::string* out) { + auto status = ReadRandomBytes(count, *out); + if (status != OK) { + LOG(ERROR) << "Random read failed with status: " << status; + return false; + } + return true; +} + +bool createSecdiscardable(const std::string& filename, std::string* hash) { + std::string secdiscardable; + if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false; + if (!writeStringToFile(secdiscardable, filename)) return false; + hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash); + return true; +} + +bool readSecdiscardable(const std::string& filename, std::string* hash) { + std::string secdiscardable; + if (!readFileToString(filename, &secdiscardable)) return false; + hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash); + return true; +} + +// static void deferedKmDeleteKey(const std::string& kmkey) { +// while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) { +// LOG(ERROR) << "Wait for boot timed out"; +// } +// Keymaster keymaster; +// if (!keymaster || !keymaster.deleteKey(kmkey)) { +// LOG(ERROR) << "Defered Key deletion failed during upgrade"; +// } +// } + +bool kmDeleteKey(Keymaster& keymaster, const std::string& kmKey) { + return true; + // bool needs_cp = cp_needsCheckpoint(); + + // if (needs_cp) { + // std::thread(deferedKmDeleteKey, kmKey).detach(); + // LOG(INFO) << "Deferring Key deletion during upgrade"; + // return true; + // } else { + // return keymaster.deleteKey(kmKey); + // } +} + +static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir, + km::KeyPurpose purpose, const km::AuthorizationSet& keyParams, + const km::AuthorizationSet& opParams, + const km::HardwareAuthToken& authToken, + km::AuthorizationSet* outParams, bool keepOld) { + auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob; + std::string kmKey; + if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation(); + km::AuthorizationSet inParams(keyParams); + inParams.append(opParams.begin(), opParams.end()); + for (;;) { + auto opHandle = keymaster.begin(purpose, kmKey, inParams, authToken, outParams); + if (opHandle) { + return opHandle; + } + if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle; + LOG(DEBUG) << "Upgrading key: " << dir; + std::string newKey; + if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation(); + // auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded; + // if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation(); + // if (!keepOld) { + // if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) { + // PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath; + // return KeymasterOperation(); + // } + // if (!android::vold::FsyncDirectory(dir)) { + // LOG(ERROR) << "Key dir sync failed: " << dir; + // return KeymasterOperation(); + // } + // if (!kmDeleteKey(keymaster, kmKey)) { + // LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir; + // } + // } + kmKey = newKey; + LOG(INFO) << "Key upgraded in memory: " << dir; + } +} + +static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir, + const km::AuthorizationSet& keyParams, + const km::HardwareAuthToken& authToken, const KeyBuffer& message, + std::string* ciphertext, bool keepOld) { + km::AuthorizationSet opParams; + km::AuthorizationSet outParams; + auto opHandle = begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken, + &outParams, keepOld); + if (!opHandle) return false; + auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE); + if (!nonceBlob.isOk()) { + LOG(ERROR) << "GCM encryption but no nonce generated"; + return false; + } + // nonceBlob here is just a pointer into existing data, must not be freed + std::string nonce(reinterpret_cast(&nonceBlob.value()[0]), + nonceBlob.value().size()); + if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false; + std::string body; + if (!opHandle.updateCompletely(message, &body)) return false; + + std::string mac; + if (!opHandle.finish(&mac)) return false; + if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false; + *ciphertext = nonce + body + mac; + return true; +} + +static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir, + const km::AuthorizationSet& keyParams, + const km::HardwareAuthToken& authToken, + const std::string& ciphertext, KeyBuffer* message, + bool keepOld) { + auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES); + auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES); + auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE, + km::support::blob2hidlVec(nonce)); + auto opHandle = begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken, + nullptr, keepOld); + if (!opHandle) return false; + if (!opHandle.updateCompletely(bodyAndMac, message)) return false; + if (!opHandle.finish(nullptr)) return false; + return true; +} + +static std::string getStretching(const KeyAuthentication& auth) { + if (!auth.usesKeymaster()) { + return kStretch_none; + } else if (auth.secret.empty()) { + return kStretch_nopassword; + } else { + char paramstr[PROPERTY_VALUE_MAX]; + + property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS); + return std::string() + kStretchPrefix_scrypt + paramstr; + } +} + +static bool stretchingNeedsSalt(const std::string& stretching) { + return stretching != kStretch_nopassword && stretching != kStretch_none; +} + +static bool stretchSecret(const std::string& stretching, const std::string& secret, + const std::string& salt, std::string* stretched) { + if (stretching == kStretch_nopassword) { + if (!secret.empty()) { + LOG(WARNING) << "Password present but stretching is nopassword"; + // Continue anyway + } + stretched->clear(); + } else if (stretching == kStretch_none) { + *stretched = secret; + } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(), + stretching.begin())) { + int Nf, rf, pf; + if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf, + &rf, &pf)) { + LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching; + return false; + } + stretched->assign(STRETCHED_BYTES, '\0'); + if (crypto_scrypt(reinterpret_cast(secret.data()), secret.size(), + reinterpret_cast(salt.data()), salt.size(), 1 << Nf, + 1 << rf, 1 << pf, reinterpret_cast(&(*stretched)[0]), + stretched->size()) != 0) { + LOG(ERROR) << "scrypt failed with params: " << stretching; + return false; + } + } else { + LOG(ERROR) << "Unknown stretching type: " << stretching; + return false; + } + return true; +} + +static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching, + const std::string& salt, const std::string& secdiscardable_hash, + std::string* appId) { + std::string stretched; + if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false; + *appId = secdiscardable_hash + stretched; + return true; +} + +static void logOpensslError() { + LOG(ERROR) << "Openssl error: " << ERR_get_error(); +} + +static bool encryptWithoutKeymaster(const std::string& preKey, const KeyBuffer& plaintext, + std::string* ciphertext) { + std::string key; + hashWithPrefix(kHashPrefix_keygen, preKey, &key); + key.resize(AES_KEY_BYTES); + if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false; + auto ctx = std::unique_ptr( + EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + if (!ctx) { + logOpensslError(); + return false; + } + if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL, + reinterpret_cast(key.data()), + reinterpret_cast(ciphertext->data()))) { + logOpensslError(); + return false; + } + ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES); + int outlen; + if (1 != EVP_EncryptUpdate( + ctx.get(), reinterpret_cast(&(*ciphertext)[0] + GCM_NONCE_BYTES), + &outlen, reinterpret_cast(plaintext.data()), plaintext.size())) { + logOpensslError(); + return false; + } + if (outlen != static_cast(plaintext.size())) { + LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen; + return false; + } + if (1 != EVP_EncryptFinal_ex( + ctx.get(), + reinterpret_cast(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()), + &outlen)) { + logOpensslError(); + return false; + } + if (outlen != 0) { + LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen; + return false; + } + if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES, + reinterpret_cast(&(*ciphertext)[0] + GCM_NONCE_BYTES + + plaintext.size()))) { + logOpensslError(); + return false; + } + return true; +} + +static bool decryptWithoutKeymaster(const std::string& preKey, const std::string& ciphertext, + KeyBuffer* plaintext) { + if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) { + LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size(); + return false; + } + std::string key; + hashWithPrefix(kHashPrefix_keygen, preKey, &key); + key.resize(AES_KEY_BYTES); + auto ctx = std::unique_ptr( + EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + if (!ctx) { + logOpensslError(); + return false; + } + if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL, + reinterpret_cast(key.data()), + reinterpret_cast(ciphertext.data()))) { + logOpensslError(); + return false; + } + *plaintext = KeyBuffer(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES); + int outlen; + if (1 != EVP_DecryptUpdate(ctx.get(), reinterpret_cast(&(*plaintext)[0]), &outlen, + reinterpret_cast(ciphertext.data() + GCM_NONCE_BYTES), + plaintext->size())) { + logOpensslError(); + return false; + } + if (outlen != static_cast(plaintext->size())) { + LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen; + return false; + } + if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES, + const_cast(reinterpret_cast( + ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) { + logOpensslError(); + return false; + } + if (1 != EVP_DecryptFinal_ex(ctx.get(), + reinterpret_cast(&(*plaintext)[0] + plaintext->size()), + &outlen)) { + logOpensslError(); + return false; + } + if (outlen != 0) { + LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen; + return false; + } + return true; +} + +bool pathExists(const std::string& path) { + return access(path.c_str(), F_OK) == 0; +} + +bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) { + if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) { + PLOG(ERROR) << "key mkdir " << dir; + return false; + } + if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false; + std::string secdiscardable_hash; + if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false; + std::string stretching = getStretching(auth); + if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false; + std::string salt; + if (stretchingNeedsSalt(stretching)) { + if (ReadRandomBytes(SALT_BYTES, salt) != OK) { + LOG(ERROR) << "Random read failed"; + return false; + } + if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false; + } + std::string appId; + if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false; + std::string encryptedKey; + if (auth.usesKeymaster()) { + Keymaster keymaster; + if (!keymaster) return false; + std::string kmKey; + if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false; + if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false; + km::AuthorizationSet keyParams; + km::HardwareAuthToken authToken; + std::tie(keyParams, authToken) = beginParams(auth, appId); + if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey, + false)) + return false; + } else { + if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false; + } + if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false; + if (!FsyncDirectory(dir)) return false; + return true; +} + +bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path, + const KeyAuthentication& auth, const KeyBuffer& key) { + if (pathExists(key_path)) { + LOG(ERROR) << "Already exists, cannot create key at: " << key_path; + return false; + } + if (pathExists(tmp_path)) { + LOG(DEBUG) << "Already exists, destroying: " << tmp_path; + destroyKey(tmp_path); // May be partially created so ignore errors + } + if (!storeKey(tmp_path, auth, key)) return false; + if (rename(tmp_path.c_str(), key_path.c_str()) != 0) { + PLOG(ERROR) << "Unable to move new key to location: " << key_path; + return false; + } + LOG(DEBUG) << "Created key: " << key_path; + return true; +} + +bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key, + bool keepOld) { + std::string version; + if (!readFileToString(dir + "/" + kFn_version, &version)) return false; + if (version != kCurrentVersion) { + LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version; + return false; + } + std::string secdiscardable_hash; + if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false; + std::string stretching; + if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false; + std::string salt; + if (stretchingNeedsSalt(stretching)) { + if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false; + } + std::string appId; + if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false; + std::string encryptedMessage; + if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false; + if (auth.usesKeymaster()) { + Keymaster keymaster; + if (!keymaster) return false; + km::AuthorizationSet keyParams; + km::HardwareAuthToken authToken; + std::tie(keyParams, authToken) = beginParams(auth, appId); + if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key, + keepOld)) + return false; + } else { + if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false; + } + return true; +} + +static bool deleteKey(const std::string& dir) { + std::string kmKey; + if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false; + Keymaster keymaster; + if (!keymaster) return false; + if (!keymaster.deleteKey(kmKey)) return false; + return true; +} + +bool runSecdiscardSingle(const std::string& file) { + if (ForkExecvp(std::vector{kSecdiscardPath, "--", file}) != 0) { + LOG(ERROR) << "secdiscard failed"; + return false; + } + return true; +} + +static bool recursiveDeleteKey(const std::string& dir) { + if (ForkExecvp(std::vector{kRmPath, "-rf", dir}) != 0) { + LOG(ERROR) << "recursive delete failed"; + return false; + } + return true; +} + +bool destroyKey(const std::string& dir) { + bool success = true; + // Try each thing, even if previous things failed. + bool uses_km = pathExists(dir + "/" + kFn_keymaster_key_blob); + if (uses_km) { + success &= deleteKey(dir); + } + auto secdiscard_cmd = std::vector{ + kSecdiscardPath, + "--", + dir + "/" + kFn_encrypted_key, + dir + "/" + kFn_secdiscardable, + }; + if (uses_km) { + secdiscard_cmd.emplace_back(dir + "/" + kFn_keymaster_key_blob); + } + if (ForkExecvp(secdiscard_cmd) != 0) { + LOG(ERROR) << "secdiscard failed"; + success = false; + } + success &= recursiveDeleteKey(dir); + return success; +} + +} // namespace vold +} // namespace android diff --git a/crypto/fscrypt/KeyStorage.h b/crypto/fscrypt/KeyStorage.h new file mode 100755 index 00000000..276b6b93 --- /dev/null +++ b/crypto/fscrypt/KeyStorage.h @@ -0,0 +1,74 @@ +/* + * 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 ANDROID_VOLD_KEYSTORAGE_H +#define ANDROID_VOLD_KEYSTORAGE_H + +#include "KeyBuffer.h" + +#include + +namespace android { +namespace vold { + +// Represents the information needed to decrypt a disk encryption key. +// If "token" is nonempty, it is passed in as a required Gatekeeper auth token. +// If "token" and "secret" are nonempty, "secret" is appended to the application-specific +// binary needed to unlock. +// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process. +class KeyAuthentication { + public: + KeyAuthentication(const std::string& t, const std::string& s) : token{t}, secret{s} {}; + + bool usesKeymaster() const { return !token.empty() || secret.empty(); }; + + const std::string token; + const std::string secret; +}; + +extern const KeyAuthentication kEmptyAuthentication; + +// Checks if path "path" exists. +bool pathExists(const std::string& path); + +bool createSecdiscardable(const std::string& path, std::string* hash); +bool readSecdiscardable(const std::string& path, std::string* hash); + +// Create a directory at the named path, and store "key" in it, +// in such a way that it can only be retrieved via Keymaster and +// can be securely deleted. +// It's safe to move/rename the directory after creation. +bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key); + +// Create a directory at the named path, and store "key" in it as storeKey +// This version creates the key in "tmp_path" then atomically renames "tmp_path" +// to "key_path" thereby ensuring that the key is either stored entirely or +// not at all. +bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path, + const KeyAuthentication& auth, const KeyBuffer& key); + +// Retrieve the key from the named directory. +bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key, + bool keepOld = false); + +// Securely destroy the key stored in the named directory and delete the directory. +bool destroyKey(const std::string& dir); + +bool runSecdiscardSingle(const std::string& file); +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/fscrypt/KeyUtil.cpp b/crypto/fscrypt/KeyUtil.cpp new file mode 100755 index 00000000..91d6b37d --- /dev/null +++ b/crypto/fscrypt/KeyUtil.cpp @@ -0,0 +1,191 @@ +/* + * 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 "KeyUtil.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "KeyStorage.h" +#include "Utils.h" + +namespace android { +namespace vold { + +constexpr int FS_AES_256_XTS_KEY_SIZE = 64; + +bool randomKey(KeyBuffer* key) { + *key = KeyBuffer(FS_AES_256_XTS_KEY_SIZE); + if (ReadRandomBytes(key->size(), key->data()) != 0) { + // TODO status_t plays badly with PLOG, fix it. + LOG(ERROR) << "Random read failed"; + return false; + } + return true; +} + +// Get raw keyref - used to make keyname and to pass to ioctl +static std::string generateKeyRef(const uint8_t* key, int length) { + SHA512_CTX c; + + SHA512_Init(&c); + SHA512_Update(&c, key, length); + unsigned char key_ref1[SHA512_DIGEST_LENGTH]; + SHA512_Final(key_ref1, &c); + + SHA512_Init(&c); + SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH); + unsigned char key_ref2[SHA512_DIGEST_LENGTH]; + SHA512_Final(key_ref2, &c); + + static_assert(FS_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH, "Hash too short for descriptor"); + return std::string((char*)key_ref2, FS_KEY_DESCRIPTOR_SIZE); +} + +static bool fillKey(const KeyBuffer& key, fscrypt_key* fs_key) { + if (key.size() != FS_AES_256_XTS_KEY_SIZE) { + LOG(ERROR) << "Wrong size key " << key.size(); + return false; + } + static_assert(FS_AES_256_XTS_KEY_SIZE <= sizeof(fs_key->raw), "Key too long!"); + fs_key->mode = FS_ENCRYPTION_MODE_AES_256_XTS; + fs_key->size = key.size(); + memset(fs_key->raw, 0, sizeof(fs_key->raw)); + memcpy(fs_key->raw, key.data(), key.size()); + return true; +} + +static char const* const NAME_PREFIXES[] = {"ext4", "f2fs", "fscrypt", nullptr}; + +static std::string keyname(const std::string& prefix, const std::string& raw_ref) { + std::ostringstream o; + o << prefix << ":"; + for (unsigned char i : raw_ref) { + o << std::hex << std::setw(2) << std::setfill('0') << (int)i; + } + return o.str(); +} + +// Get the keyring we store all keys in +static bool fscryptKeyring(key_serial_t* device_keyring) { + *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "fscrypt", 0); + if (*device_keyring == -1) { + PLOG(ERROR) << "Unable to find device keyring"; + return false; + } + return true; +} + +// Install password into global keyring +// Return raw key reference for use in policy +bool installKey(const KeyBuffer& key, std::string* raw_ref) { + // Place fscrypt_key into automatically zeroing buffer. + KeyBuffer fsKeyBuffer(sizeof(fscrypt_key)); + fscrypt_key& fs_key = *reinterpret_cast(fsKeyBuffer.data()); + + if (!fillKey(key, &fs_key)) return false; + *raw_ref = generateKeyRef(fs_key.raw, fs_key.size); + key_serial_t device_keyring; + if (!fscryptKeyring(&device_keyring)) return false; + for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) { + auto ref = keyname(*name_prefix, *raw_ref); + key_serial_t key_id = + add_key("logon", ref.c_str(), (void*)&fs_key, sizeof(fs_key), device_keyring); + if (key_id == -1) { + PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring; + return false; + } + LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring + << " in process " << getpid(); + } + return true; +} + +bool evictKey(const std::string& raw_ref) { + key_serial_t device_keyring; + if (!fscryptKeyring(&device_keyring)) return false; + bool success = true; + for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) { + auto ref = keyname(*name_prefix, raw_ref); + auto key_serial = keyctl_search(device_keyring, "logon", ref.c_str(), 0); + + // Unlink the key from the keyring. Prefer unlinking to revoking or + // invalidating, since unlinking is actually no less secure currently, and + // it avoids bugs in certain kernel versions where the keyring key is + // referenced from places it shouldn't be. + if (keyctl_unlink(key_serial, device_keyring) != 0) { + PLOG(ERROR) << "Failed to unlink key with serial " << key_serial << " ref " << ref; + success = false; + } else { + LOG(DEBUG) << "Unlinked key with serial " << key_serial << " ref " << ref; + } + } + return success; +} + +bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication, + const std::string& key_path, const std::string& tmp_path, + std::string* key_ref) { + KeyBuffer key; + if (pathExists(key_path)) { + LOG(DEBUG) << "Key exists, using: " << key_path; + if (!retrieveKey(key_path, key_authentication, &key)) return false; + } else { + if (!create_if_absent) { + LOG(ERROR) << "No key found in " << key_path; + return false; + } + LOG(INFO) << "Creating new key in " << key_path; + if (!randomKey(&key)) return false; + if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false; + } + + if (!installKey(key, key_ref)) { + LOG(ERROR) << "Failed to install key in " << key_path; + return false; + } + return true; +} + +bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path, + KeyBuffer* key, bool keepOld) { + LOG(ERROR) << "retreiveKey1"; + if (pathExists(key_path)) { + LOG(ERROR) << "Key exists, using: " << key_path; + if (!retrieveKey(key_path, kEmptyAuthentication, key, keepOld)) return false; + } else { + if (!create_if_absent) { + LOG(ERROR) << "No key found in " << key_path; + return false; + } + LOG(ERROR) << "Creating new key in " << key_path; + if (!randomKey(key)) return false; + LOG(ERROR) << "retrieveKey1"; + if (!storeKeyAtomically(key_path, tmp_path, kEmptyAuthentication, *key)) return false; + } + return true; +} + +} // namespace vold +} // namespace android diff --git a/crypto/fscrypt/KeyUtil.h b/crypto/fscrypt/KeyUtil.h new file mode 100755 index 00000000..7ee67254 --- /dev/null +++ b/crypto/fscrypt/KeyUtil.h @@ -0,0 +1,41 @@ +/* + * 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 ANDROID_VOLD_KEYUTIL_H +#define ANDROID_VOLD_KEYUTIL_H + +#include "KeyBuffer.h" +#include "KeyStorage.h" + +#include +#include + +namespace android { +namespace vold { + +bool randomKey(KeyBuffer* key); +bool installKey(const KeyBuffer& key, std::string* raw_ref); +bool evictKey(const std::string& raw_ref); +bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication, + const std::string& key_path, const std::string& tmp_path, + std::string* key_ref); +bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path, + KeyBuffer* key, bool keepOld = true); + +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/fscrypt/Keymaster.cpp b/crypto/fscrypt/Keymaster.cpp new file mode 100755 index 00000000..aad43874 --- /dev/null +++ b/crypto/fscrypt/Keymaster.cpp @@ -0,0 +1,346 @@ +/* + * 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 "Keymaster.h" + +#include +#include +#include + +namespace android { +namespace vold { + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::keymaster::V4_0::SecurityLevel; + +KeymasterOperation::~KeymasterOperation() { + if (mDevice) mDevice->abort(mOpHandle); +} + +bool KeymasterOperation::updateCompletely(const char* input, size_t inputLen, + const std::function consumer) { + uint32_t inputConsumed = 0; + + km::ErrorCode km_error; + auto hidlCB = [&](km::ErrorCode ret, uint32_t inputConsumedDelta, + const hidl_vec& /*ignored*/, + const hidl_vec& _output) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + inputConsumed += inputConsumedDelta; + consumer(reinterpret_cast(&_output[0]), _output.size()); + }; + + while (inputConsumed != inputLen) { + size_t toRead = static_cast(inputLen - inputConsumed); + auto inputBlob = km::support::blob2hidlVec( + reinterpret_cast(&input[inputConsumed]), toRead); + auto error = mDevice->update(mOpHandle, hidl_vec(), inputBlob, + km::HardwareAuthToken(), km::VerificationToken(), hidlCB); + if (!error.isOk()) { + LOG(ERROR) << "update failed: " << error.description(); + mDevice = nullptr; + return false; + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "update failed, code " << int32_t(km_error); + mDevice = nullptr; + return false; + } + if (inputConsumed > inputLen) { + LOG(ERROR) << "update reported too much input consumed"; + mDevice = nullptr; + return false; + } + } + return true; +} + +bool KeymasterOperation::finish(std::string* output) { + km::ErrorCode km_error; + auto hidlCb = [&](km::ErrorCode ret, const hidl_vec& /*ignored*/, + const hidl_vec& _output) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + if (output) output->assign(reinterpret_cast(&_output[0]), _output.size()); + }; + auto error = mDevice->finish(mOpHandle, hidl_vec(), hidl_vec(), + hidl_vec(), km::HardwareAuthToken(), + km::VerificationToken(), hidlCb); + mDevice = nullptr; + if (!error.isOk()) { + LOG(ERROR) << "finish failed: " << error.description(); + return false; + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "finish failed, code " << int32_t(km_error); + return false; + } + return true; +} + +/* static */ bool Keymaster::hmacKeyGenerated = false; + +Keymaster::Keymaster() { + auto devices = KmDevice::enumerateAvailableDevices(); + if (!hmacKeyGenerated) { + KmDevice::performHmacKeyAgreement(devices); + hmacKeyGenerated = true; + } + for (auto& dev : devices) { + // Do not use StrongBox for device encryption / credential encryption. If a security chip + // is present it will have Weaver, which already strengthens CE. We get no additional + // benefit from using StrongBox here, so skip it. + if (dev->halVersion().securityLevel != SecurityLevel::STRONGBOX) { + mDevice = std::move(dev); + break; + } + } + if (!mDevice) return; + auto& version = mDevice->halVersion(); + LOG(INFO) << "Using " << version.keymasterName << " from " << version.authorName + << " for encryption. Security level: " << toString(version.securityLevel) + << ", HAL: " << mDevice->descriptor() << "/" << mDevice->instanceName(); +} + +bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* key) { + km::ErrorCode km_error; + auto hidlCb = [&](km::ErrorCode ret, const hidl_vec& keyBlob, + const km::KeyCharacteristics& /*ignored*/) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + if (key) key->assign(reinterpret_cast(&keyBlob[0]), keyBlob.size()); + }; + + auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb); + if (!error.isOk()) { + LOG(ERROR) << "generate_key failed: " << error.description(); + return false; + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "generate_key failed, code " << int32_t(km_error); + return false; + } + return true; +} + +bool Keymaster::deleteKey(const std::string& key) { + auto keyBlob = km::support::blob2hidlVec(key); + auto error = mDevice->deleteKey(keyBlob); + if (!error.isOk()) { + LOG(ERROR) << "delete_key failed: " << error.description(); + return false; + } + if (error != km::ErrorCode::OK) { + LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error)); + return false; + } + return true; +} + +bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams, + std::string* newKey) { + auto oldKeyBlob = km::support::blob2hidlVec(oldKey); + km::ErrorCode km_error; + auto hidlCb = [&](km::ErrorCode ret, const hidl_vec& upgradedKeyBlob) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + if (newKey) + newKey->assign(reinterpret_cast(&upgradedKeyBlob[0]), + upgradedKeyBlob.size()); + }; + auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb); + if (!error.isOk()) { + LOG(ERROR) << "upgrade_key failed: " << error.description(); + return false; + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error); + return false; + } + return true; +} + +KeymasterOperation Keymaster::begin(km::KeyPurpose purpose, const std::string& key, + const km::AuthorizationSet& inParams, + const km::HardwareAuthToken& authToken, + km::AuthorizationSet* outParams) { + auto keyBlob = km::support::blob2hidlVec(key); + uint64_t mOpHandle; + km::ErrorCode km_error; + + auto hidlCb = [&](km::ErrorCode ret, const hidl_vec& _outParams, + uint64_t operationHandle) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + if (outParams) *outParams = _outParams; + mOpHandle = operationHandle; + }; + + auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), authToken, hidlCb); + if (!error.isOk()) { + LOG(ERROR) << "begin failed: " << error.description(); + return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR); + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "begin failed, code " << int32_t(km_error); + return KeymasterOperation(km_error); + } + return KeymasterOperation(mDevice.get(), mOpHandle); +} + +bool Keymaster::isSecure() { + return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE; +} + +} // namespace vold +} // namespace android + +using namespace ::android::vold; + +int keymaster_compatibility_cryptfs_scrypt() { + Keymaster dev; + if (!dev) { + LOG(ERROR) << "Failed to initiate keymaster session"; + return -1; + } + return dev.isSecure(); +} + +static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size, + uint32_t* out_size) { + if (!buffer || !out_size) { + LOG(ERROR) << "Missing target pointers"; + return false; + } + *out_size = towrite.size(); + if (buffer_size < towrite.size()) { + LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size(); + return false; + } + memset(buffer, '\0', buffer_size); + std::copy(towrite.begin(), towrite.end(), buffer); + return true; +} + +static km::AuthorizationSet keyParams(uint32_t rsa_key_size, uint64_t rsa_exponent, + uint32_t ratelimit) { + return km::AuthorizationSetBuilder() + .RsaSigningKey(rsa_key_size, rsa_exponent) + .NoDigestOrPadding() + .Authorization(km::TAG_BLOB_USAGE_REQUIREMENTS, km::KeyBlobUsageRequirements::STANDALONE) + .Authorization(km::TAG_NO_AUTH_REQUIRED) + .Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit); +} + +int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent, + uint32_t ratelimit, uint8_t* key_buffer, + uint32_t key_buffer_size, uint32_t* key_out_size) { + if (key_out_size) { + *key_out_size = 0; + } + Keymaster dev; + if (!dev) { + LOG(ERROR) << "Failed to initiate keymaster session"; + return -1; + } + std::string key; + if (!dev.generateKey(keyParams(rsa_key_size, rsa_exponent, ratelimit), &key)) return -1; + if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1; + return 0; +} + +int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent, + uint32_t ratelimit, const uint8_t* key_blob, + size_t key_blob_size, uint8_t* key_buffer, + uint32_t key_buffer_size, uint32_t* key_out_size) { + if (key_out_size) { + *key_out_size = 0; + } + Keymaster dev; + if (!dev) { + LOG(ERROR) << "Failed to initiate keymaster session"; + return -1; + } + std::string old_key(reinterpret_cast(key_blob), key_blob_size); + std::string new_key; + if (!dev.upgradeKey(old_key, keyParams(rsa_key_size, rsa_exponent, ratelimit), &new_key)) + return -1; + if (!write_string_to_buf(new_key, key_buffer, key_buffer_size, key_out_size)) return -1; + return 0; +} + +KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt( + const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object, + const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size) { + Keymaster dev; + if (!dev) { + LOG(ERROR) << "Failed to initiate keymaster session"; + return KeymasterSignResult::error; + } + if (!key_blob || !object || !signature_buffer || !signature_buffer_size) { + LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument"; + return KeymasterSignResult::error; + } + + km::AuthorizationSet outParams; + std::string key(reinterpret_cast(key_blob), key_blob_size); + std::string input(reinterpret_cast(object), object_size); + std::string output; + KeymasterOperation op; + + auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding(); + while (true) { + op = dev.begin(km::KeyPurpose::SIGN, key, paramBuilder, km::HardwareAuthToken(), &outParams); + if (op.errorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) { + sleep(ratelimit); + continue; + } else + break; + } + + if (op.errorCode() == km::ErrorCode::KEY_REQUIRES_UPGRADE) { + LOG(ERROR) << "Keymaster key requires upgrade"; + return KeymasterSignResult::upgrade; + } + + if (op.errorCode() != km::ErrorCode::OK) { + LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode()); + return KeymasterSignResult::error; + } + + if (!op.updateCompletely(input, &output)) { + LOG(ERROR) << "Error sending data to keymaster signature transaction: " + << uint32_t(op.errorCode()); + return KeymasterSignResult::error; + } + + if (!op.finish(&output)) { + LOG(ERROR) << "Error finalizing keymaster signature transaction: " + << int32_t(op.errorCode()); + return KeymasterSignResult::error; + } + + *signature_buffer = reinterpret_cast(malloc(output.size())); + if (*signature_buffer == nullptr) { + LOG(ERROR) << "Error allocation buffer for keymaster signature"; + return KeymasterSignResult::error; + } + *signature_buffer_size = output.size(); + std::copy(output.data(), output.data() + output.size(), *signature_buffer); + return KeymasterSignResult::ok; +} diff --git a/crypto/fscrypt/Keymaster.h b/crypto/fscrypt/Keymaster.h new file mode 100644 index 00000000..42a2b5d4 --- /dev/null +++ b/crypto/fscrypt/Keymaster.h @@ -0,0 +1,159 @@ +/* + * 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 ANDROID_VOLD_KEYMASTER_H +#define ANDROID_VOLD_KEYMASTER_H + +#include "KeyBuffer.h" + +#include +#include +#include + +#include +#include +#include + +namespace android { +namespace vold { + +namespace km = ::android::hardware::keymaster::V4_0; +using KmDevice = km::support::Keymaster; + +// C++ wrappers to the Keymaster hidl interface. +// This is tailored to the needs of KeyStorage, but could be extended to be +// a more general interface. + +// Wrapper for a Keymaster operation handle representing an +// ongoing Keymaster operation. Aborts the operation +// in the destructor if it is unfinished. Methods log failures +// to LOG(ERROR). +class KeymasterOperation { + public: + ~KeymasterOperation(); + // Is this instance valid? This is false if creation fails, and becomes + // false on finish or if an update fails. + explicit operator bool() const { return mError == km::ErrorCode::OK; } + km::ErrorCode errorCode() const { return mError; } + // Call "update" repeatedly until all of the input is consumed, and + // concatenate the output. Return true on success. + template + bool updateCompletely(TI& input, TO* output) { + if (output) output->clear(); + return updateCompletely(input.data(), input.size(), [&](const char* b, size_t n) { + if (output) std::copy(b, b + n, std::back_inserter(*output)); + }); + } + + // Finish and write the output to this string, unless pointer is null. + bool finish(std::string* output); + // Move constructor + KeymasterOperation(KeymasterOperation&& rhs) { *this = std::move(rhs); } + // Construct an object in an error state for error returns + KeymasterOperation() : mDevice{nullptr}, mOpHandle{0}, mError{km::ErrorCode::UNKNOWN_ERROR} {} + // Move Assignment + KeymasterOperation& operator=(KeymasterOperation&& rhs) { + mDevice = rhs.mDevice; + rhs.mDevice = nullptr; + + mOpHandle = rhs.mOpHandle; + rhs.mOpHandle = 0; + + mError = rhs.mError; + rhs.mError = km::ErrorCode::UNKNOWN_ERROR; + + return *this; + } + + private: + KeymasterOperation(KmDevice* d, uint64_t h) + : mDevice{d}, mOpHandle{h}, mError{km::ErrorCode::OK} {} + KeymasterOperation(km::ErrorCode error) : mDevice{nullptr}, mOpHandle{0}, mError{error} {} + + bool updateCompletely(const char* input, size_t inputLen, + const std::function consumer); + + KmDevice* mDevice; + uint64_t mOpHandle; + km::ErrorCode mError; + DISALLOW_COPY_AND_ASSIGN(KeymasterOperation); + friend class Keymaster; +}; + +// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not +// part of one. +class Keymaster { + public: + Keymaster(); + // false if we failed to open the keymaster device. + explicit operator bool() { return mDevice.get() != nullptr; } + // Generate a key in the keymaster from the given params. + bool generateKey(const km::AuthorizationSet& inParams, std::string* key); + // If the keymaster supports it, permanently delete a key. + bool deleteKey(const std::string& key); + // Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE. + bool upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams, + std::string* newKey); + // Begin a new cryptographic operation, collecting output parameters if pointer is non-null + KeymasterOperation begin(km::KeyPurpose purpose, const std::string& key, + const km::AuthorizationSet& inParams, + const km::HardwareAuthToken& authToken, + km::AuthorizationSet* outParams); + bool isSecure(); + + private: + std::unique_ptr mDevice; + DISALLOW_COPY_AND_ASSIGN(Keymaster); + static bool hmacKeyGenerated; +}; + +} // namespace vold +} // namespace android + +// FIXME no longer needed now cryptfs is in C++. + +/* + * The following functions provide C bindings to keymaster services + * needed by cryptfs scrypt. The compatibility check checks whether + * the keymaster implementation is considered secure, i.e., TEE backed. + * The create_key function generates an RSA key for signing. + * The sign_object function signes an object with the given keymaster + * key. + */ + +/* Return values for keymaster_sign_object_for_cryptfs_scrypt */ + +enum class KeymasterSignResult { + ok = 0, + error = -1, + upgrade = -2, +}; + +int keymaster_compatibility_cryptfs_scrypt(); +int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent, + uint32_t ratelimit, uint8_t* key_buffer, + uint32_t key_buffer_size, uint32_t* key_out_size); + +int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent, + uint32_t ratelimit, const uint8_t* key_blob, + size_t key_blob_size, uint8_t* key_buffer, + uint32_t key_buffer_size, uint32_t* key_out_size); + +KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt( + const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object, + const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size); + +#endif diff --git a/crypto/fscrypt/MetadataCrypt.cpp b/crypto/fscrypt/MetadataCrypt.cpp new file mode 100755 index 00000000..45f3af32 --- /dev/null +++ b/crypto/fscrypt/MetadataCrypt.cpp @@ -0,0 +1,304 @@ +/* + * 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 "MetadataCrypt.h" +#include "KeyBuffer.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "Checkpoint.h" +#include "EncryptInplace.h" +#include "KeyStorage.h" +#include "KeyUtil.h" +#include "Keymaster.h" +#include "Utils.h" +#include "VoldUtil.h" + +#define DM_CRYPT_BUF_SIZE 4096 +#define TABLE_LOAD_RETRIES 10 +#define DEFAULT_KEY_TARGET_TYPE "default-key" + +using android::fs_mgr::FstabEntry; +using android::fs_mgr::GetEntryForMountPoint; +using android::fs_mgr::ReadDefaultFstab; +using android::vold::KeyBuffer; + +static const std::string kDmNameUserdata = "userdata"; + +static const char* kFn_keymaster_key_blob = "keymaster_key_blob"; +static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded"; + +static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) { + // fs_mgr_do_mount runs fsck. Use setexeccon to run trusted + // partitions in the fsck domain. + if (setexeccon(android::vold::sFsckContext)) { + PLOG(ERROR) << "Failed to setexeccon"; + return false; + } + auto mount_rc = fs_mgr_do_mount(&fstab_default, const_cast(mount_point), + const_cast(blk_device), nullptr, + false); + if (setexeccon(nullptr)) { + PLOG(ERROR) << "Failed to clear setexeccon"; + return false; + } + if (mount_rc != 0) { + LOG(ERROR) << "fs_mgr_do_mount failed with rc " << mount_rc; + return false; + } + LOG(DEBUG) << "Mounted " << mount_point; + return true; +} + +android::fs_mgr::Fstab fstab_default; + +namespace android { +namespace vold { + +// Note: It is possible to orphan a key if it is removed before deleting +// Update this once keymaster APIs change, and we have a proper commit. +static void commit_key(const std::string& dir) { + while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) { + LOG(ERROR) << "Wait for boot timed out"; + } + Keymaster keymaster; + auto keyPath = dir + "/" + kFn_keymaster_key_blob; + auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded; + std::string key; + + if (!android::base::ReadFileToString(keyPath, &key)) { + LOG(ERROR) << "Failed to read old key: " << dir; + return; + } + if (rename(newKeyPath.c_str(), keyPath.c_str()) != 0) { + PLOG(ERROR) << "Unable to move upgraded key to location: " << keyPath; + return; + } + if (!keymaster.deleteKey(key)) { + LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir; + } + LOG(INFO) << "Old Key deleted: " << dir; +} + +static bool read_key(const FstabEntry& data_rec, bool create_if_absent, KeyBuffer* key) { + if (data_rec.key_dir.empty()) { + LOG(ERROR) << "Failed to get key_dir"; + return false; + } + std::string key_dir = data_rec.key_dir; + std::string sKey; + auto dir = key_dir + "/key"; + LOG(DEBUG) << "key_dir/key: " << dir; + if (fs_mkdirs(dir.c_str(), 0700)) { + PLOG(ERROR) << "Creating directories: " << dir; + return false; + } + auto temp = key_dir + "/tmp"; + auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded; + /* If we have a leftover upgraded key, delete it. + * We either failed an update and must return to the old key, + * or we rebooted before commiting the keys in a freak accident. + * Either way, we can re-upgrade the key if we need to. + */ + Keymaster keymaster; + if (pathExists(newKeyPath)) { + if (!android::base::ReadFileToString(newKeyPath, &sKey)) + LOG(ERROR) << "Failed to read old key: " << dir; + else if (!keymaster.deleteKey(sKey)) + LOG(ERROR) << "Old key deletion failed, continuing anyway: " << dir; + else + unlink(newKeyPath.c_str()); + } + // bool needs_cp = cp_needsCheckpoint(); + bool needs_cp = false; + if (!android::vold::retrieveKey(create_if_absent, dir, temp, key, needs_cp)) return false; + if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach(); + return true; +} + +} // namespace vold +} // namespace android + +static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) { + KeyBuffer hex_key; + if (android::vold::StrToHex(key, hex_key) != android::OK) { + LOG(ERROR) << "Failed to turn key to hex"; + return KeyBuffer(); + } + auto res = KeyBuffer() + "AES-256-XTS " + hex_key + " " + real_blkdev.c_str() + " 0"; + return res; +} + +static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t* nr_sec) { + if (android::vold::GetBlockDev512Sectors(real_blkdev, nr_sec) != android::OK) { + PLOG(ERROR) << "Unable to measure size of " << real_blkdev; + return false; + } + return true; +} + +static struct dm_ioctl* dm_ioctl_init(char* buffer, size_t buffer_size, const std::string& dm_name) { + if (buffer_size < sizeof(dm_ioctl)) { + LOG(ERROR) << "dm_ioctl buffer too small"; + return nullptr; + } + + memset(buffer, 0, buffer_size); + struct dm_ioctl* io = (struct dm_ioctl*)buffer; + io->data_size = buffer_size; + io->data_start = sizeof(struct dm_ioctl); + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + io->flags = 0; + dm_name.copy(io->name, sizeof(io->name)); + return io; +} + +static bool create_crypto_blk_dev(const std::string& dm_name, uint64_t nr_sec, + const std::string& target_type, const KeyBuffer& crypt_params, + std::string* crypto_blkdev) { + android::base::unique_fd dm_fd( + TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC, 0))); + if (dm_fd == -1) { + PLOG(ERROR) << "Cannot open device-mapper"; + return false; + } + alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE]; + auto io = dm_ioctl_init(buffer, sizeof(buffer), dm_name); + if (!io || ioctl(dm_fd.get(), DM_DEV_CREATE, io) != 0) { + PLOG(ERROR) << "Cannot create dm-crypt device " << dm_name; + return false; + } + + // Get the device status, in particular, the name of its device file + io = dm_ioctl_init(buffer, sizeof(buffer), dm_name); + if (ioctl(dm_fd.get(), DM_DEV_STATUS, io) != 0) { + PLOG(ERROR) << "Cannot retrieve dm-crypt device status " << dm_name; + return false; + } + *crypto_blkdev = std::string() + "/dev/block/dm-" + + std::to_string((io->dev & 0xff) | ((io->dev >> 12) & 0xfff00)); + + io = dm_ioctl_init(buffer, sizeof(buffer), dm_name); + size_t paramix = io->data_start + sizeof(struct dm_target_spec); + size_t nullix = paramix + crypt_params.size(); + size_t endix = (nullix + 1 + 7) & 8; // Add room for \0 and align to 8 byte boundary + + if (endix > sizeof(buffer)) { + LOG(ERROR) << "crypt_params too big for DM_CRYPT_BUF_SIZE"; + return false; + } + + io->target_count = 1; + auto tgt = (struct dm_target_spec*)(buffer + io->data_start); + tgt->status = 0; + tgt->sector_start = 0; + tgt->length = nr_sec; + target_type.copy(tgt->target_type, sizeof(tgt->target_type)); + memcpy(buffer + paramix, crypt_params.data(), + std::min(crypt_params.size(), sizeof(buffer) - paramix)); + buffer[nullix] = '\0'; + tgt->next = endix; + + for (int i = 0;; i++) { + if (ioctl(dm_fd.get(), DM_TABLE_LOAD, io) == 0) { + break; + } + if (i + 1 >= TABLE_LOAD_RETRIES) { + PLOG(ERROR) << "DM_TABLE_LOAD ioctl failed"; + return false; + } + PLOG(INFO) << "DM_TABLE_LOAD ioctl failed, retrying"; + usleep(500000); + } + + // Resume this device to activate it + io = dm_ioctl_init(buffer, sizeof(buffer), dm_name); + if (ioctl(dm_fd.get(), DM_DEV_SUSPEND, io)) { + PLOG(ERROR) << "Cannot resume dm-crypt device " << dm_name; + return false; + } + return true; +} + +bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::string& mount_point, + bool needs_encrypt) { + LOG(ERROR) << "fscrypt_mount_metadata_encrypted: " << blk_device << " " << mount_point << " " << needs_encrypt; + // auto encrypted_state = android::base::GetProperty("ro.crypto.state", ""); + // if (encrypted_state != "") { + // LOG(ERROR) << "fscrypt_enable_crypto got unexpected starting state: " << encrypted_state; + // return false; + // } + + if (!ReadDefaultFstab(&fstab_default)) { + PLOG(ERROR) << "Failed to open default fstab"; + return -1; + } + + auto data_rec = GetEntryForMountPoint(&fstab_default, mount_point); + if (!data_rec) { + LOG(ERROR) << "Failed to get data_rec"; + return false; + } + KeyBuffer key; + if (!read_key(*data_rec, needs_encrypt, &key)) return false; + uint64_t nr_sec; + if (!get_number_of_sectors(data_rec->blk_device, &nr_sec)) return false; + std::string crypto_blkdev; + if (!create_crypto_blk_dev(kDmNameUserdata, nr_sec, DEFAULT_KEY_TARGET_TYPE, + default_key_params(blk_device, key), &crypto_blkdev)) + return false; + // FIXME handle the corrupt case + if (needs_encrypt) { + LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec; + off64_t size_already_done = 0; + auto rc = cryptfs_enable_inplace(crypto_blkdev.data(), blk_device.data(), nr_sec, + &size_already_done, nr_sec, 0, false); + if (rc != 0) { + LOG(ERROR) << "Inplace crypto failed with code: " << rc; + return false; + } + if (static_cast(size_already_done) != nr_sec) { + LOG(ERROR) << "Inplace crypto only got up to sector: " << size_already_done; + return false; + } + LOG(INFO) << "Inplace encryption complete"; + } + + LOG(ERROR) << "Mounting metadata-encrypted filesystem:" << mount_point; + mount_via_fs_mgr(data_rec->mount_point.c_str(), crypto_blkdev.c_str()); + android::base::SetProperty("ro.crypto.fs_crypto_blkdev", crypto_blkdev); + return true; +} diff --git a/crypto/fscrypt/MetadataCrypt.h b/crypto/fscrypt/MetadataCrypt.h new file mode 100644 index 00000000..cd0f5e57 --- /dev/null +++ b/crypto/fscrypt/MetadataCrypt.h @@ -0,0 +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. + */ + +#ifndef _METADATA_CRYPT_H +#define _METADATA_CRYPT_H + +#include + +bool fscrypt_mount_metadata_encrypted(const std::string& block_device, + const std::string& mount_point, bool needs_encrypt); + +#endif diff --git a/crypto/fscrypt/Process.cpp b/crypto/fscrypt/Process.cpp new file mode 100644 index 00000000..3d8e3d74 --- /dev/null +++ b/crypto/fscrypt/Process.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "Process.h" + +using android::base::StringPrintf; + +namespace android { +namespace vold { + +static bool checkMaps(const std::string& path, const std::string& prefix) { + bool found = false; + auto file = std::unique_ptr{fopen(path.c_str(), "re"), fclose}; + if (!file) { + return false; + } + + char* buf = nullptr; + size_t len = 0; + while (getline(&buf, &len, file.get()) != -1) { + std::string line(buf); + std::string::size_type pos = line.find('/'); + if (pos != std::string::npos) { + line = line.substr(pos); + if (android::base::StartsWith(line, prefix)) { + LOG(WARNING) << "Found map " << path << " referencing " << line; + found = true; + break; + } + } + } + free(buf); + + return found; +} + +static bool checkSymlink(const std::string& path, const std::string& prefix) { + std::string res; + if (android::base::Readlink(path, &res)) { + if (android::base::StartsWith(res, prefix)) { + LOG(WARNING) << "Found symlink " << path << " referencing " << res; + return true; + } + } + return false; +} + +int KillProcessesWithOpenFiles(const std::string& prefix, int signal) { + std::unordered_set pids; + + auto proc_d = std::unique_ptr(opendir("/proc"), closedir); + if (!proc_d) { + PLOG(ERROR) << "Failed to open proc"; + return -1; + } + + struct dirent* proc_de; + while ((proc_de = readdir(proc_d.get())) != nullptr) { + // We only care about valid PIDs + pid_t pid; + if (proc_de->d_type != DT_DIR) continue; + if (!android::base::ParseInt(proc_de->d_name, &pid)) continue; + + // Look for references to prefix + bool found = false; + auto path = StringPrintf("/proc/%d", pid); + found |= checkMaps(path + "/maps", prefix); + found |= checkSymlink(path + "/cwd", prefix); + found |= checkSymlink(path + "/root", prefix); + found |= checkSymlink(path + "/exe", prefix); + + auto fd_path = path + "/fd"; + auto fd_d = std::unique_ptr(opendir(fd_path.c_str()), closedir); + if (!fd_d) { + PLOG(WARNING) << "Failed to open " << fd_path; + } else { + struct dirent* fd_de; + while ((fd_de = readdir(fd_d.get())) != nullptr) { + if (fd_de->d_type != DT_LNK) continue; + found |= checkSymlink(fd_path + "/" + fd_de->d_name, prefix); + } + } + + if (found) { + pids.insert(pid); + } + } + if (signal != 0) { + for (const auto& pid : pids) { + LOG(WARNING) << "Sending " << strsignal(signal) << " to " << pid; + kill(pid, signal); + } + } + return pids.size(); +} + +} // namespace vold +} // namespace android diff --git a/crypto/fscrypt/Process.h b/crypto/fscrypt/Process.h new file mode 100644 index 00000000..14067820 --- /dev/null +++ b/crypto/fscrypt/Process.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008 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 _PROCESS_H +#define _PROCESS_H + +namespace android { +namespace vold { + +int KillProcessesWithOpenFiles(const std::string& path, int signal); + +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/fscrypt/ScryptParameters.cpp b/crypto/fscrypt/ScryptParameters.cpp new file mode 100644 index 00000000..669809b9 --- /dev/null +++ b/crypto/fscrypt/ScryptParameters.cpp @@ -0,0 +1,50 @@ +/* + * 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 "ScryptParameters.h" + +#include +#include + +bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf) { + int params[3]; + char *token; + char *saveptr; + int i; + + /* + * The token we're looking for should be three integers separated by + * colons (e.g., "12:8:1"). Scan the property to make sure it matches. + */ + for (i = 0, token = strtok_r(const_cast(paramstr), ":", &saveptr); + token != nullptr && i < 3; + i++, token = strtok_r(nullptr, ":", &saveptr)) { + char *endptr; + params[i] = strtol(token, &endptr, 10); + + /* + * Check that there was a valid number and it's 8-bit. + */ + if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) { + return false; + } + } + if (token != nullptr) { + return false; + } + *Nf = params[0]; *rf = params[1]; *pf = params[2]; + return true; +} diff --git a/crypto/fscrypt/ScryptParameters.h b/crypto/fscrypt/ScryptParameters.h new file mode 100644 index 00000000..1b43ea57 --- /dev/null +++ b/crypto/fscrypt/ScryptParameters.h @@ -0,0 +1,32 @@ +/* + * 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 ANDROID_VOLD_SCRYPT_PARAMETERS_H +#define ANDROID_VOLD_SCRYPT_PARAMETERS_H + +#include +#include + +#define SCRYPT_PROP "ro.crypto.scrypt_params" +#define SCRYPT_DEFAULTS "15:3:1" + +__BEGIN_DECLS + +bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf); + +__END_DECLS + +#endif diff --git a/crypto/fscrypt/Utils.cpp b/crypto/fscrypt/Utils.cpp new file mode 100755 index 00000000..aa71d8fb --- /dev/null +++ b/crypto/fscrypt/Utils.cpp @@ -0,0 +1,989 @@ +/* + * 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 "Utils.h" + +#include "Process.h" +#include "sehandle.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef UMOUNT_NOFOLLOW +#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */ +#endif + +using namespace std::chrono_literals; +using android::base::ReadFileToString; +using android::base::StringPrintf; + +namespace android { +namespace vold { + +security_context_t sBlkidContext = nullptr; +security_context_t sBlkidUntrustedContext = nullptr; +security_context_t sFsckContext = nullptr; +security_context_t sFsckUntrustedContext = nullptr; +struct selabel_handle* sehandle; + +bool sSleepOnUnmount = true; + +static const char* kBlkidPath = "/system/bin/blkid"; +static const char* kKeyPath = "/data/misc/vold"; + +static const char* kProcFilesystems = "/proc/filesystems"; + +// Lock used to protect process-level SELinux changes from racing with each +// other between multiple threads. +static std::mutex kSecurityLock; + +status_t CreateDeviceNode(const std::string& path, dev_t dev) { + std::lock_guard lock(kSecurityLock); + const char* cpath = path.c_str(); + status_t res = 0; + + char* secontext = nullptr; + if (sehandle) { + if (!selabel_lookup(sehandle, &secontext, cpath, S_IFBLK)) { + setfscreatecon(secontext); + } + } + + mode_t mode = 0660 | S_IFBLK; + if (mknod(cpath, mode, dev) < 0) { + if (errno != EEXIST) { + PLOG(ERROR) << "Failed to create device node for " << major(dev) << ":" << minor(dev) + << " at " << path; + res = -errno; + } + } + + if (secontext) { + setfscreatecon(nullptr); + freecon(secontext); + } + + return res; +} + +status_t DestroyDeviceNode(const std::string& path) { + const char* cpath = path.c_str(); + if (TEMP_FAILURE_RETRY(unlink(cpath))) { + return -errno; + } else { + return OK; + } +} + +status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) { + std::lock_guard lock(kSecurityLock); + const char* cpath = path.c_str(); + + char* secontext = nullptr; + if (sehandle) { + if (!selabel_lookup(sehandle, &secontext, cpath, S_IFDIR)) { + setfscreatecon(secontext); + } + } + + int res = fs_prepare_dir(cpath, mode, uid, gid); + + if (secontext) { + setfscreatecon(nullptr); + freecon(secontext); + } + + if (res == 0) { + return OK; + } else { + return -errno; + } +} + +status_t ForceUnmount(const std::string& path) { + const char* cpath = path.c_str(); + if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) { + return OK; + } + // Apps might still be handling eject request, so wait before + // we start sending signals + if (sSleepOnUnmount) sleep(5); + + KillProcessesWithOpenFiles(path, SIGINT); + if (sSleepOnUnmount) sleep(5); + if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) { + return OK; + } + + KillProcessesWithOpenFiles(path, SIGTERM); + if (sSleepOnUnmount) sleep(5); + if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) { + return OK; + } + + KillProcessesWithOpenFiles(path, SIGKILL); + if (sSleepOnUnmount) sleep(5); + if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) { + return OK; + } + + return -errno; +} + +status_t KillProcessesUsingPath(const std::string& path) { + if (KillProcessesWithOpenFiles(path, SIGINT) == 0) { + return OK; + } + if (sSleepOnUnmount) sleep(5); + + if (KillProcessesWithOpenFiles(path, SIGTERM) == 0) { + return OK; + } + if (sSleepOnUnmount) sleep(5); + + if (KillProcessesWithOpenFiles(path, SIGKILL) == 0) { + return OK; + } + if (sSleepOnUnmount) sleep(5); + + // Send SIGKILL a second time to determine if we've + // actually killed everyone with open files + if (KillProcessesWithOpenFiles(path, SIGKILL) == 0) { + return OK; + } + PLOG(ERROR) << "Failed to kill processes using " << path; + return -EBUSY; +} + +status_t BindMount(const std::string& source, const std::string& target) { + if (UnmountTree(target) < 0) { + return -errno; + } + if (TEMP_FAILURE_RETRY(mount(source.c_str(), target.c_str(), nullptr, MS_BIND, nullptr)) < 0) { + PLOG(ERROR) << "Failed to bind mount " << source << " to " << target; + return -errno; + } + return OK; +} + +status_t Symlink(const std::string& target, const std::string& linkpath) { + if (Unlink(linkpath) < 0) { + return -errno; + } + if (TEMP_FAILURE_RETRY(symlink(target.c_str(), linkpath.c_str())) < 0) { + PLOG(ERROR) << "Failed to create symlink " << linkpath << " to " << target; + return -errno; + } + return OK; +} + +status_t Unlink(const std::string& linkpath) { + if (TEMP_FAILURE_RETRY(unlink(linkpath.c_str())) < 0 && errno != EINVAL && errno != ENOENT) { + PLOG(ERROR) << "Failed to unlink " << linkpath; + return -errno; + } + return OK; +} + +status_t CreateDir(const std::string& dir, mode_t mode) { + struct stat sb; + if (TEMP_FAILURE_RETRY(stat(dir.c_str(), &sb)) == 0) { + if (S_ISDIR(sb.st_mode)) { + return OK; + } else if (TEMP_FAILURE_RETRY(unlink(dir.c_str())) == -1) { + PLOG(ERROR) << "Failed to unlink " << dir; + return -errno; + } + } else if (errno != ENOENT) { + PLOG(ERROR) << "Failed to stat " << dir; + return -errno; + } + if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), mode)) == -1 && errno != EEXIST) { + PLOG(ERROR) << "Failed to mkdir " << dir; + return -errno; + } + return OK; +} + +bool FindValue(const std::string& raw, const std::string& key, std::string* value) { + auto qual = key + "=\""; + size_t start = 0; + while (true) { + start = raw.find(qual, start); + if (start == std::string::npos) return false; + if (start == 0 || raw[start - 1] == ' ') { + break; + } + start += 1; + } + start += qual.length(); + + auto end = raw.find("\"", start); + if (end == std::string::npos) return false; + + *value = raw.substr(start, end - start); + return true; +} + +static status_t readMetadata(const std::string& path, std::string* fsType, std::string* fsUuid, + std::string* fsLabel, bool untrusted) { + fsType->clear(); + fsUuid->clear(); + fsLabel->clear(); + + std::vector cmd; + cmd.push_back(kBlkidPath); + cmd.push_back("-c"); + cmd.push_back("/dev/null"); + cmd.push_back("-s"); + cmd.push_back("TYPE"); + cmd.push_back("-s"); + cmd.push_back("UUID"); + cmd.push_back("-s"); + cmd.push_back("LABEL"); + cmd.push_back(path); + + std::vector output; + status_t res = ForkExecvp(cmd, &output, untrusted ? sBlkidUntrustedContext : sBlkidContext); + if (res != OK) { + LOG(WARNING) << "blkid failed to identify " << path; + return res; + } + + for (const auto& line : output) { + // Extract values from blkid output, if defined + FindValue(line, "TYPE", fsType); + FindValue(line, "UUID", fsUuid); + FindValue(line, "LABEL", fsLabel); + } + + return OK; +} + +status_t ReadMetadata(const std::string& path, std::string* fsType, std::string* fsUuid, + std::string* fsLabel) { + return readMetadata(path, fsType, fsUuid, fsLabel, false); +} + +status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType, std::string* fsUuid, + std::string* fsLabel) { + return readMetadata(path, fsType, fsUuid, fsLabel, true); +} + +static std::vector ConvertToArgv(const std::vector& args) { + std::vector argv; + argv.reserve(args.size() + 1); + for (const auto& arg : args) { + if (argv.empty()) { + LOG(DEBUG) << arg; + } else { + LOG(DEBUG) << " " << arg; + } + argv.emplace_back(arg.data()); + } + argv.emplace_back(nullptr); + return argv; +} + +static status_t ReadLinesFromFdAndLog(std::vector* output, + android::base::unique_fd ufd) { + std::unique_ptr fp(android::base::Fdopen(std::move(ufd), "r"), fclose); + if (!fp) { + PLOG(ERROR) << "fdopen in ReadLinesFromFdAndLog"; + return -errno; + } + if (output) output->clear(); + char line[1024]; + while (fgets(line, sizeof(line), fp.get()) != nullptr) { + LOG(DEBUG) << line; + if (output) output->emplace_back(line); + } + return OK; +} + +status_t ForkExecvp(const std::vector& args, std::vector* output, + security_context_t context) { + auto argv = ConvertToArgv(args); + + android::base::unique_fd pipe_read, pipe_write; + if (!android::base::Pipe(&pipe_read, &pipe_write)) { + PLOG(ERROR) << "Pipe in ForkExecvp"; + return -errno; + } + + pid_t pid = fork(); + if (pid == 0) { + if (context) { + if (setexeccon(context)) { + LOG(ERROR) << "Failed to setexeccon in ForkExecvp"; + abort(); + } + } + pipe_read.reset(); + if (dup2(pipe_write.get(), STDOUT_FILENO) == -1) { + PLOG(ERROR) << "dup2 in ForkExecvp"; + _exit(EXIT_FAILURE); + } + pipe_write.reset(); + execvp(argv[0], const_cast(argv.data())); + PLOG(ERROR) << "exec in ForkExecvp" << " cmd: " << argv[0]; + _exit(EXIT_FAILURE); + } + if (pid == -1) { + PLOG(ERROR) << "fork in ForkExecvp"; + return -errno; + } + + pipe_write.reset(); + auto st = ReadLinesFromFdAndLog(output, std::move(pipe_read)); + if (st != 0) return st; + + int status; + if (waitpid(pid, &status, 0) == -1) { + PLOG(ERROR) << "waitpid in ForkExecvp"; + return -errno; + } + if (!WIFEXITED(status)) { + LOG(ERROR) << "Process did not exit normally, status: " << status; + return -ECHILD; + } + if (WEXITSTATUS(status)) { + LOG(ERROR) << "Process exited with code: " << WEXITSTATUS(status); + return WEXITSTATUS(status); + } + return OK; +} + +pid_t ForkExecvpAsync(const std::vector& args) { + auto argv = ConvertToArgv(args); + + pid_t pid = fork(); + if (pid == 0) { + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + execvp(argv[0], const_cast(argv.data())); + PLOG(ERROR) << "exec in ForkExecvpAsync"; + _exit(EXIT_FAILURE); + } + if (pid == -1) { + PLOG(ERROR) << "fork in ForkExecvpAsync"; + return -1; + } + return pid; +} + +status_t ReadRandomBytes(size_t bytes, std::string& out) { + out.resize(bytes); + return ReadRandomBytes(bytes, &out[0]); +} + +status_t ReadRandomBytes(size_t bytes, char* buf) { + int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + if (fd == -1) { + return -errno; + } + + ssize_t n; + while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], bytes))) > 0) { + bytes -= n; + buf += n; + } + close(fd); + + if (bytes == 0) { + return OK; + } else { + return -EIO; + } +} + +status_t GenerateRandomUuid(std::string& out) { + status_t res = ReadRandomBytes(16, out); + if (res == OK) { + out[6] &= 0x0f; /* clear version */ + out[6] |= 0x40; /* set to version 4 */ + out[8] &= 0x3f; /* clear variant */ + out[8] |= 0x80; /* set to IETF variant */ + } + return res; +} + +status_t HexToStr(const std::string& hex, std::string& str) { + str.clear(); + bool even = true; + char cur = 0; + for (size_t i = 0; i < hex.size(); i++) { + int val = 0; + switch (hex[i]) { + // clang-format off + case ' ': case '-': case ':': continue; + case 'f': case 'F': val = 15; break; + case 'e': case 'E': val = 14; break; + case 'd': case 'D': val = 13; break; + case 'c': case 'C': val = 12; break; + case 'b': case 'B': val = 11; break; + case 'a': case 'A': val = 10; break; + case '9': val = 9; break; + case '8': val = 8; break; + case '7': val = 7; break; + case '6': val = 6; break; + case '5': val = 5; break; + case '4': val = 4; break; + case '3': val = 3; break; + case '2': val = 2; break; + case '1': val = 1; break; + case '0': val = 0; break; + default: return -EINVAL; + // clang-format on + } + + if (even) { + cur = val << 4; + } else { + cur += val; + str.push_back(cur); + cur = 0; + } + even = !even; + } + return even ? OK : -EINVAL; +} + +static const char* kLookup = "0123456789abcdef"; + +status_t StrToHex(const std::string& str, std::string& hex) { + hex.clear(); + for (size_t i = 0; i < str.size(); i++) { + hex.push_back(kLookup[(str[i] & 0xF0) >> 4]); + hex.push_back(kLookup[str[i] & 0x0F]); + } + return OK; +} + +status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex) { + hex.clear(); + for (size_t i = 0; i < str.size(); i++) { + hex.push_back(kLookup[(str.data()[i] & 0xF0) >> 4]); + hex.push_back(kLookup[str.data()[i] & 0x0F]); + } + return OK; +} + +status_t NormalizeHex(const std::string& in, std::string& out) { + std::string tmp; + if (HexToStr(in, tmp)) { + return -EINVAL; + } + return StrToHex(tmp, out); +} + +status_t GetBlockDevSize(int fd, uint64_t* size) { + if (ioctl(fd, BLKGETSIZE64, size)) { + return -errno; + } + + return OK; +} + +status_t GetBlockDevSize(const std::string& path, uint64_t* size) { + int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + status_t res = OK; + + if (fd < 0) { + return -errno; + } + + res = GetBlockDevSize(fd, size); + + close(fd); + + return res; +} + +status_t GetBlockDev512Sectors(const std::string& path, uint64_t* nr_sec) { + uint64_t size; + status_t res = GetBlockDevSize(path, &size); + + if (res != OK) { + return res; + } + + *nr_sec = size / 512; + + return OK; +} + +uint64_t GetFreeBytes(const std::string& path) { + struct statvfs sb; + if (statvfs(path.c_str(), &sb) == 0) { + return (uint64_t)sb.f_bavail * sb.f_frsize; + } else { + return -1; + } +} + +// TODO: borrowed from frameworks/native/libs/diskusage/ which should +// eventually be migrated into system/ +static int64_t stat_size(struct stat* s) { + int64_t blksize = s->st_blksize; + // count actual blocks used instead of nominal file size + int64_t size = s->st_blocks * 512; + + if (blksize) { + /* round up to filesystem block size */ + size = (size + blksize - 1) & (~(blksize - 1)); + } + + return size; +} + +// TODO: borrowed from frameworks/native/libs/diskusage/ which should +// eventually be migrated into system/ +int64_t calculate_dir_size(int dfd) { + int64_t size = 0; + struct stat s; + DIR* d; + struct dirent* de; + + d = fdopendir(dfd); + if (d == NULL) { + close(dfd); + return 0; + } + + while ((de = readdir(d))) { + const char* name = de->d_name; + if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { + size += stat_size(&s); + } + if (de->d_type == DT_DIR) { + int subfd; + + /* always skip "." and ".." */ + if (name[0] == '.') { + if (name[1] == 0) continue; + if ((name[1] == '.') && (name[2] == 0)) continue; + } + + subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (subfd >= 0) { + size += calculate_dir_size(subfd); + } + } + } + closedir(d); + return size; +} + +uint64_t GetTreeBytes(const std::string& path) { + int dirfd = open(path.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (dirfd < 0) { + PLOG(WARNING) << "Failed to open " << path; + return -1; + } else { + return calculate_dir_size(dirfd); + } +} + +bool IsFilesystemSupported(const std::string& fsType) { + std::string supported; + if (!ReadFileToString(kProcFilesystems, &supported)) { + PLOG(ERROR) << "Failed to read supported filesystems"; + return false; + } + return supported.find(fsType + "\n") != std::string::npos; +} + +status_t WipeBlockDevice(const std::string& path) { + status_t res = -1; + const char* c_path = path.c_str(); + uint64_t range[2] = {0, 0}; + + int fd = TEMP_FAILURE_RETRY(open(c_path, O_RDWR | O_CLOEXEC)); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << path; + goto done; + } + + if (GetBlockDevSize(fd, &range[1]) != OK) { + PLOG(ERROR) << "Failed to determine size of " << path; + goto done; + } + + LOG(INFO) << "About to discard " << range[1] << " on " << path; + if (ioctl(fd, BLKDISCARD, &range) == 0) { + LOG(INFO) << "Discard success on " << path; + res = 0; + } else { + PLOG(ERROR) << "Discard failure on " << path; + } + +done: + close(fd); + return res; +} + +static bool isValidFilename(const std::string& name) { + if (name.empty() || (name == ".") || (name == "..") || (name.find('/') != std::string::npos)) { + return false; + } else { + return true; + } +} + +std::string BuildKeyPath(const std::string& partGuid) { + return StringPrintf("%s/expand_%s.key", kKeyPath, partGuid.c_str()); +} + +std::string BuildDataSystemLegacyPath(userid_t userId) { + return StringPrintf("%s/system/users/%u", BuildDataPath("").c_str(), userId); +} + +std::string BuildDataSystemCePath(userid_t userId) { + return StringPrintf("%s/system_ce/%u", BuildDataPath("").c_str(), userId); +} + +std::string BuildDataSystemDePath(userid_t userId) { + return StringPrintf("%s/system_de/%u", BuildDataPath("").c_str(), userId); +} + +std::string BuildDataMiscLegacyPath(userid_t userId) { + return StringPrintf("%s/misc/user/%u", BuildDataPath("").c_str(), userId); +} + +std::string BuildDataMiscCePath(userid_t userId) { + return StringPrintf("%s/misc_ce/%u", BuildDataPath("").c_str(), userId); +} + +std::string BuildDataMiscDePath(userid_t userId) { + return StringPrintf("%s/misc_de/%u", BuildDataPath("").c_str(), userId); +} + +// Keep in sync with installd (frameworks/native/cmds/installd/utils.h) +std::string BuildDataProfilesDePath(userid_t userId) { + return StringPrintf("%s/misc/profiles/cur/%u", BuildDataPath("").c_str(), userId); +} + +std::string BuildDataVendorCePath(userid_t userId) { + return StringPrintf("%s/vendor_ce/%u", BuildDataPath("").c_str(), userId); +} + +std::string BuildDataVendorDePath(userid_t userId) { + return StringPrintf("%s/vendor_de/%u", BuildDataPath("").c_str(), userId); +} + +std::string BuildDataPath(const std::string& volumeUuid) { + // TODO: unify with installd path generation logic + if (volumeUuid.empty()) { + return "/data"; + } else { + CHECK(isValidFilename(volumeUuid)); + return StringPrintf("/mnt/expand/%s", volumeUuid.c_str()); + } +} + +std::string BuildDataMediaCePath(const std::string& volumeUuid, userid_t userId) { + // TODO: unify with installd path generation logic + std::string data(BuildDataPath(volumeUuid)); + return StringPrintf("%s/media/%u", data.c_str(), userId); +} + +std::string BuildDataUserCePath(const std::string& volumeUuid, userid_t userId) { + // TODO: unify with installd path generation logic + std::string data(BuildDataPath(volumeUuid)); + if (volumeUuid.empty() && userId == 0) { + std::string legacy = StringPrintf("%s/data", data.c_str()); + struct stat sb; + if (lstat(legacy.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) { + /* /data/data is dir, return /data/data for legacy system */ + return legacy; + } + } + return StringPrintf("%s/user/%u", data.c_str(), userId); +} + +std::string BuildDataUserDePath(const std::string& volumeUuid, userid_t userId) { + // TODO: unify with installd path generation logic + std::string data(BuildDataPath(volumeUuid)); + return StringPrintf("%s/user_de/%u", data.c_str(), userId); +} + +dev_t GetDevice(const std::string& path) { + struct stat sb; + if (stat(path.c_str(), &sb)) { + PLOG(WARNING) << "Failed to stat " << path; + return 0; + } else { + return sb.st_dev; + } +} + +status_t RestoreconRecursive(const std::string& path) { + LOG(DEBUG) << "Starting restorecon of " << path; + + static constexpr const char* kRestoreconString = "selinux.restorecon_recursive"; + + android::base::SetProperty(kRestoreconString, ""); + android::base::SetProperty(kRestoreconString, path); + + android::base::WaitForProperty(kRestoreconString, path); + + LOG(DEBUG) << "Finished restorecon of " << path; + return OK; +} + +bool Readlinkat(int dirfd, const std::string& path, std::string* result) { + // Shamelessly borrowed from android::base::Readlink() + result->clear(); + + // Most Linux file systems (ext2 and ext4, say) limit symbolic links to + // 4095 bytes. Since we'll copy out into the string anyway, it doesn't + // waste memory to just start there. We add 1 so that we can recognize + // whether it actually fit (rather than being truncated to 4095). + std::vector buf(4095 + 1); + while (true) { + ssize_t size = readlinkat(dirfd, path.c_str(), &buf[0], buf.size()); + // Unrecoverable error? + if (size == -1) return false; + // It fit! (If size == buf.size(), it may have been truncated.) + if (static_cast(size) < buf.size()) { + result->assign(&buf[0], size); + return true; + } + // Double our buffer and try again. + buf.resize(buf.size() * 2); + } +} + +bool IsRunningInEmulator() { + return android::base::GetBoolProperty("ro.kernel.qemu", false); +} + +static status_t findMountPointsWithPrefix(const std::string& prefix, + std::list& mountPoints) { + // Add a trailing slash if the client didn't provide one so that we don't match /foo/barbaz + // when the prefix is /foo/bar + std::string prefixWithSlash(prefix); + if (prefix.back() != '/') { + android::base::StringAppendF(&prefixWithSlash, "/"); + } + + std::unique_ptr mnts(setmntent("/proc/mounts", "re"), endmntent); + if (!mnts) { + PLOG(ERROR) << "Unable to open /proc/mounts"; + return -errno; + } + + // Some volumes can be stacked on each other, so force unmount in + // reverse order to give us the best chance of success. + struct mntent* mnt; // getmntent returns a thread local, so it's safe. + while ((mnt = getmntent(mnts.get())) != nullptr) { + auto mountPoint = std::string(mnt->mnt_dir) + "/"; + if (android::base::StartsWith(mountPoint, prefixWithSlash)) { + mountPoints.push_front(mountPoint); + } + } + return OK; +} + +// Unmount all mountpoints that start with prefix. prefix itself doesn't need to be a mountpoint. +status_t UnmountTreeWithPrefix(const std::string& prefix) { + std::list toUnmount; + status_t result = findMountPointsWithPrefix(prefix, toUnmount); + if (result < 0) { + return result; + } + for (const auto& path : toUnmount) { + if (umount2(path.c_str(), MNT_DETACH)) { + PLOG(ERROR) << "Failed to unmount " << path; + result = -errno; + } + } + return result; +} + +status_t UnmountTree(const std::string& mountPoint) { + if (TEMP_FAILURE_RETRY(umount2(mountPoint.c_str(), MNT_DETACH)) < 0 && errno != EINVAL && + errno != ENOENT) { + PLOG(ERROR) << "Failed to unmount " << mountPoint; + return -errno; + } + return OK; +} + +static status_t delete_dir_contents(DIR* dir) { + // Shamelessly borrowed from android::installd + int dfd = dirfd(dir); + if (dfd < 0) { + return -errno; + } + + status_t result = OK; + struct dirent* de; + while ((de = readdir(dir))) { + const char* name = de->d_name; + if (de->d_type == DT_DIR) { + /* always skip "." and ".." */ + if (name[0] == '.') { + if (name[1] == 0) continue; + if ((name[1] == '.') && (name[2] == 0)) continue; + } + + android::base::unique_fd subfd( + openat(dfd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)); + if (subfd.get() == -1) { + PLOG(ERROR) << "Couldn't openat " << name; + result = -errno; + continue; + } + std::unique_ptr subdirp( + android::base::Fdopendir(std::move(subfd)), closedir); + if (!subdirp) { + PLOG(ERROR) << "Couldn't fdopendir " << name; + result = -errno; + continue; + } + result = delete_dir_contents(subdirp.get()); + if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) { + PLOG(ERROR) << "Couldn't unlinkat " << name; + result = -errno; + } + } else { + if (unlinkat(dfd, name, 0) < 0) { + PLOG(ERROR) << "Couldn't unlinkat " << name; + result = -errno; + } + } + } + return result; +} + +status_t DeleteDirContentsAndDir(const std::string& pathname) { + status_t res = DeleteDirContents(pathname); + if (res < 0) { + return res; + } + if (TEMP_FAILURE_RETRY(rmdir(pathname.c_str())) < 0 && errno != ENOENT) { + PLOG(ERROR) << "rmdir failed on " << pathname; + return -errno; + } + LOG(VERBOSE) << "Success: rmdir on " << pathname; + return OK; +} + +status_t DeleteDirContents(const std::string& pathname) { + // Shamelessly borrowed from android::installd + std::unique_ptr dirp(opendir(pathname.c_str()), closedir); + if (!dirp) { + if (errno == ENOENT) { + return OK; + } + PLOG(ERROR) << "Failed to opendir " << pathname; + return -errno; + } + return delete_dir_contents(dirp.get()); +} + +// TODO(118708649): fix duplication with init/util.h +status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout) { + android::base::Timer t; + while (t.duration() < timeout) { + struct stat sb; + if (stat(filename, &sb) != -1) { + LOG(INFO) << "wait for '" << filename << "' took " << t; + return 0; + } + std::this_thread::sleep_for(10ms); + } + LOG(WARNING) << "wait for '" << filename << "' timed out and took " << t; + return -1; +} + +bool FsyncDirectory(const std::string& dirname) { + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dirname.c_str(), O_RDONLY | O_CLOEXEC))); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << dirname; + return false; + } + if (fsync(fd) == -1) { + if (errno == EROFS || errno == EINVAL) { + PLOG(WARNING) << "Skip fsync " << dirname + << " on a file system does not support synchronization"; + } else { + PLOG(ERROR) << "Failed to fsync " << dirname; + return false; + } + } + return true; +} + +bool writeStringToFile(const std::string& payload, const std::string& filename) { + android::base::unique_fd fd(TEMP_FAILURE_RETRY( + open(filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0666))); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << filename; + return false; + } + if (!android::base::WriteStringToFd(payload, fd)) { + PLOG(ERROR) << "Failed to write to " << filename; + unlink(filename.c_str()); + return false; + } + // fsync as close won't guarantee flush data + // see close(2), fsync(2) and b/68901441 + if (fsync(fd) == -1) { + if (errno == EROFS || errno == EINVAL) { + PLOG(WARNING) << "Skip fsync " << filename + << " on a file system does not support synchronization"; + } else { + PLOG(ERROR) << "Failed to fsync " << filename; + unlink(filename.c_str()); + return false; + } + } + return true; +} + +} // namespace vold +} // namespace android diff --git a/crypto/fscrypt/Utils.h b/crypto/fscrypt/Utils.h new file mode 100755 index 00000000..af4e401b --- /dev/null +++ b/crypto/fscrypt/Utils.h @@ -0,0 +1,153 @@ +/* + * 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. + */ + +#ifndef ANDROID_VOLD_UTILS_H +#define ANDROID_VOLD_UTILS_H + +#include "KeyBuffer.h" + +#include +#include +#include +#include + +#include +#include +#include + +struct DIR; + +namespace android { +namespace vold { + +/* SELinux contexts used depending on the block device type */ +extern security_context_t sBlkidContext; +extern security_context_t sBlkidUntrustedContext; +extern security_context_t sFsckContext; +extern security_context_t sFsckUntrustedContext; + +// TODO remove this with better solution, b/64143519 +extern bool sSleepOnUnmount; + +status_t CreateDeviceNode(const std::string& path, dev_t dev); +status_t DestroyDeviceNode(const std::string& path); + +/* fs_prepare_dir wrapper that creates with SELinux context */ +status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid); + +/* Really unmounts the path, killing active processes along the way */ +status_t ForceUnmount(const std::string& path); + +/* Kills any processes using given path */ +status_t KillProcessesUsingPath(const std::string& path); + +/* Creates bind mount from source to target */ +status_t BindMount(const std::string& source, const std::string& target); + +/** Creates a symbolic link to target */ +status_t Symlink(const std::string& target, const std::string& linkpath); + +/** Calls unlink(2) at linkpath */ +status_t Unlink(const std::string& linkpath); + +/** Creates the given directory if it is not already available */ +status_t CreateDir(const std::string& dir, mode_t mode); + +bool FindValue(const std::string& raw, const std::string& key, std::string* value); + +/* Reads filesystem metadata from device at path */ +status_t ReadMetadata(const std::string& path, std::string* fsType, std::string* fsUuid, + std::string* fsLabel); + +/* Reads filesystem metadata from untrusted device at path */ +status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType, std::string* fsUuid, + std::string* fsLabel); + +/* Returns either WEXITSTATUS() status, or a negative errno */ +status_t ForkExecvp(const std::vector& args, std::vector* output = nullptr, + security_context_t context = nullptr); + +pid_t ForkExecvpAsync(const std::vector& args); + +/* Gets block device size in bytes */ +status_t GetBlockDevSize(int fd, uint64_t* size); +status_t GetBlockDevSize(const std::string& path, uint64_t* size); +/* Gets block device size in 512 byte sectors */ +status_t GetBlockDev512Sectors(const std::string& path, uint64_t* nr_sec); + +status_t ReadRandomBytes(size_t bytes, std::string& out); +status_t ReadRandomBytes(size_t bytes, char* buffer); +status_t GenerateRandomUuid(std::string& out); + +/* Converts hex string to raw bytes, ignoring [ :-] */ +status_t HexToStr(const std::string& hex, std::string& str); +/* Converts raw bytes to hex string */ +status_t StrToHex(const std::string& str, std::string& hex); +/* Converts raw key bytes to hex string */ +status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex); +/* Normalize given hex string into consistent format */ +status_t NormalizeHex(const std::string& in, std::string& out); + +uint64_t GetFreeBytes(const std::string& path); +uint64_t GetTreeBytes(const std::string& path); + +bool IsFilesystemSupported(const std::string& fsType); + +/* Wipes contents of block device at given path */ +status_t WipeBlockDevice(const std::string& path); + +std::string BuildKeyPath(const std::string& partGuid); + +std::string BuildDataSystemLegacyPath(userid_t userid); +std::string BuildDataSystemCePath(userid_t userid); +std::string BuildDataSystemDePath(userid_t userid); +std::string BuildDataMiscLegacyPath(userid_t userid); +std::string BuildDataMiscCePath(userid_t userid); +std::string BuildDataMiscDePath(userid_t userid); +std::string BuildDataProfilesDePath(userid_t userid); +std::string BuildDataVendorCePath(userid_t userid); +std::string BuildDataVendorDePath(userid_t userid); + +std::string BuildDataPath(const std::string& volumeUuid); +std::string BuildDataMediaCePath(const std::string& volumeUuid, userid_t userid); +std::string BuildDataUserCePath(const std::string& volumeUuid, userid_t userid); +std::string BuildDataUserDePath(const std::string& volumeUuid, userid_t userid); + +dev_t GetDevice(const std::string& path); + +status_t RestoreconRecursive(const std::string& path); + +// TODO: promote to android::base +bool Readlinkat(int dirfd, const std::string& path, std::string* result); + +/* Checks if Android is running in QEMU */ +bool IsRunningInEmulator(); + +status_t UnmountTreeWithPrefix(const std::string& prefix); +status_t UnmountTree(const std::string& mountPoint); + +status_t DeleteDirContentsAndDir(const std::string& pathname); +status_t DeleteDirContents(const std::string& pathname); + +status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout); + +bool FsyncDirectory(const std::string& dirname); + +bool writeStringToFile(const std::string& payload, const std::string& filename); +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/fscrypt/VoldUtil.cpp b/crypto/fscrypt/VoldUtil.cpp new file mode 100644 index 00000000..082f7434 --- /dev/null +++ b/crypto/fscrypt/VoldUtil.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2013 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 "VoldUtil.h" + +android::fs_mgr::Fstab fstab_default; diff --git a/crypto/fscrypt/VoldUtil.h b/crypto/fscrypt/VoldUtil.h new file mode 100644 index 00000000..173c5986 --- /dev/null +++ b/crypto/fscrypt/VoldUtil.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 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. + */ + +#pragma once + +#include +#include + +extern android::fs_mgr::Fstab fstab_default; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) diff --git a/crypto/fscrypt/Weaver1.cpp b/crypto/fscrypt/Weaver1.cpp new file mode 100644 index 00000000..ea357edc --- /dev/null +++ b/crypto/fscrypt/Weaver1.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2017 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. + */ + +/* To the best of my knowledge there is no native implementation for + * Weaver so I made this by looking at the IWeaver.h file that gets + * compiled by the build system. I took the information from this header + * file and looked at keymaster source to get an idea of the proper way + * to write the functions. + */ + +#include "Weaver1.h" + +//#include +//#include +//#include +//#include + +#include + +#include +#define ERROR 1 +#define LOG(x) std::cout + +using namespace android::hardware::weaver; +using android::hardware::hidl_string; +using ::android::hardware::weaver::V1_0::IWeaver; +using ::android::hardware::weaver::V1_0::WeaverConfig; +using ::android::hardware::weaver::V1_0::WeaverReadStatus; +using ::android::hardware::weaver::V1_0::WeaverReadResponse; +using ::android::hardware::weaver::V1_0::WeaverStatus; +using ::android::hardware::Return; +using ::android::sp; + +namespace android { +namespace vold { + +Weaver::Weaver() { + mDevice = ::android::hardware::weaver::V1_0::IWeaver::getService(); + GottenConfig = false; +} + +bool Weaver::GetConfig() { + if (GottenConfig) + return true; + + WeaverStatus status; + WeaverConfig cfg; + + bool callbackCalled = false; + auto ret = mDevice->getConfig([&](WeaverStatus s, WeaverConfig c) { + callbackCalled = true; + status = s; + cfg = c; + }); + if (ret.isOk() && callbackCalled && status == WeaverStatus::OK) { + config = cfg; + GottenConfig = true; + return true; + } + return false; +} + +bool Weaver::GetSlots(uint32_t* slots) { + if (!GetConfig()) + return false; + *slots = config.slots; + return true; +} + +bool Weaver::GetKeySize(uint32_t* keySize) { + if (!GetConfig()) + return false; + *keySize = config.keySize; + return true; +} + +bool Weaver::GetValueSize(uint32_t* valueSize) { + if (!GetConfig()) + return false; + *valueSize = config.valueSize; + return true; +} + +// TODO: we should return more information about the status including time delays before the next retry +bool Weaver::WeaverVerify(const uint32_t slot, const void* weaver_key, std::vector* payload) { + bool callbackCalled = false; + WeaverReadStatus status; + std::vector readValue; + uint32_t timeout; + uint32_t keySize; + if (!GetKeySize(&keySize)) + return false; + std::vector key; + key.resize(keySize); + uint32_t index = 0; + unsigned char* ptr = (unsigned char*)weaver_key; + for (index = 0; index < keySize; index++) { + key[index] = *ptr; + ptr++; + } + const auto readRet = mDevice->read(slot, key, [&](WeaverReadStatus s, WeaverReadResponse r) { + callbackCalled = true; + status = s; + readValue = r.value; + timeout = r.timeout; + }); + if (readRet.isOk() && callbackCalled && status == WeaverReadStatus::OK && timeout == 0) { + *payload = readValue; + return true; + } + return false; +} + +} // namespace vold +} // namespace android diff --git a/crypto/fscrypt/Weaver1.h b/crypto/fscrypt/Weaver1.h new file mode 100644 index 00000000..22f401e7 --- /dev/null +++ b/crypto/fscrypt/Weaver1.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 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. + */ + +/* To the best of my knowledge there is no native implementation for + * Weaver so I made this by looking at the IWeaver.h file that gets + * compiled by the build system. I took the information from this header + * file and looked at keymaster source to get an idea of the proper way + * to write the functions. + */ + +#ifndef TWRP_WEAVER_H +#define TWRP_WEAVER_H + +#include +#include +#include + +#include +#include "Utils.h" + +namespace android { +namespace vold { +using ::android::hardware::weaver::V1_0::IWeaver; + +// Wrapper for a Weaver device +class Weaver { + public: + Weaver(); + // false if we failed to open the weaver device. + explicit operator bool() { return mDevice.get() != nullptr; } + + bool GetSlots(uint32_t* slots); + bool GetKeySize(uint32_t* keySize); + bool GetValueSize(uint32_t* valueSize); + // TODO: we should return more information about the status including time delays before the next retry + bool WeaverVerify(const uint32_t slot, const void* weaver_key, std::vector* payload); + + private: + sp mDevice; + hardware::weaver::V1_0::WeaverConfig config; + bool GottenConfig; + + bool GetConfig(); + + DISALLOW_COPY_AND_ASSIGN(Weaver); +}; + +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/fscrypt/cryptfs.h b/crypto/fscrypt/cryptfs.h new file mode 100644 index 00000000..692d7ee6 --- /dev/null +++ b/crypto/fscrypt/cryptfs.h @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2010 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 ANDROID_VOLD_CRYPTFS_H +#define ANDROID_VOLD_CRYPTFS_H + +/* This structure starts 16,384 bytes before the end of a hardware + * partition that is encrypted, or in a separate partition. It's location + * is specified by a property set in init..rc. + * The structure allocates 48 bytes for a key, but the real key size is + * specified in the struct. Currently, the code is hardcoded to use 128 + * bit keys. + * The fields after salt are only valid in rev 1.1 and later stuctures. + * Obviously, the filesystem does not include the last 16 kbytes + * of the partition if the crypt_mnt_ftr lives at the end of the + * partition. + */ + +#include +#include +#include + +#include + +/* The current cryptfs version */ +#define CURRENT_MAJOR_VERSION 1 +#define CURRENT_MINOR_VERSION 3 + +#define CRYPT_FOOTER_OFFSET 0x4000 +#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000 +#define CRYPT_PERSIST_DATA_SIZE 0x1000 + +#define MAX_CRYPTO_TYPE_NAME_LEN 64 + +#define MAX_KEY_LEN 48 +#define SALT_LEN 16 +#define SCRYPT_LEN 32 + +/* definitions of flags in the structure below */ +#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */ +#define CRYPT_ENCRYPTION_IN_PROGRESS \ + 0x2 /* Encryption partially completed, \ + encrypted_upto valid*/ +#define CRYPT_INCONSISTENT_STATE \ + 0x4 /* Set when starting encryption, clear when \ + exit cleanly, either through success or \ + correctly marked partial encryption */ +#define CRYPT_DATA_CORRUPT \ + 0x8 /* Set when encryption is fine, but the \ + underlying volume is corrupt */ +#define CRYPT_FORCE_ENCRYPTION \ + 0x10 /* Set when it is time to encrypt this \ + volume on boot. Everything in this \ + structure is set up correctly as \ + though device is encrypted except \ + that the master key is encrypted with the \ + default password. */ +#define CRYPT_FORCE_COMPLETE \ + 0x20 /* Set when the above encryption cycle is \ + complete. On next cryptkeeper entry, match \ + the password. If it matches fix the master \ + key and remove this flag. */ + +/* Allowed values for type in the structure below */ +#define CRYPT_TYPE_PASSWORD \ + 0 /* master_key is encrypted with a password \ + * Must be zero to be compatible with pre-L \ + * devices where type is always password.*/ +#define CRYPT_TYPE_DEFAULT \ + 1 /* master_key is encrypted with default \ + * password */ +#define CRYPT_TYPE_PATTERN 2 /* master_key is encrypted with a pattern */ +#define CRYPT_TYPE_PIN 3 /* master_key is encrypted with a pin */ +#define CRYPT_TYPE_MAX_TYPE 3 /* type cannot be larger than this value */ + +#define CRYPT_MNT_MAGIC 0xD0B5B1C4 +#define PERSIST_DATA_MAGIC 0xE950CD44 + +/* Key Derivation Function algorithms */ +#define KDF_PBKDF2 1 +#define KDF_SCRYPT 2 +/* Algorithms 3 & 4 deprecated before shipping outside of google, so removed */ +#define KDF_SCRYPT_KEYMASTER 5 + +/* Maximum allowed keymaster blob size. */ +#define KEYMASTER_BLOB_SIZE 2048 + +/* __le32 and __le16 defined in system/extras/ext4_utils/ext4_utils.h */ +#define __le8 unsigned char + +#if !defined(SHA256_DIGEST_LENGTH) +#define SHA256_DIGEST_LENGTH 32 +#endif + +struct crypt_mnt_ftr { + __le32 magic; /* See above */ + __le16 major_version; + __le16 minor_version; + __le32 ftr_size; /* in bytes, not including key following */ + __le32 flags; /* See above */ + __le32 keysize; /* in bytes */ + __le32 crypt_type; /* how master_key is encrypted. Must be a + * CRYPT_TYPE_XXX value */ + __le64 fs_size; /* Size of the encrypted fs, in 512 byte sectors */ + __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and + mount, set to 0 on successful mount */ + unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption + needed to decrypt this + partition, null terminated */ + __le32 spare2; /* ignored */ + unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */ + unsigned char salt[SALT_LEN]; /* The salt used for this encryption */ + __le64 persist_data_offset[2]; /* Absolute offset to both copies of crypt_persist_data + * on device with that info, either the footer of the + * real_blkdevice or the metadata partition. */ + + __le32 persist_data_size; /* The number of bytes allocated to each copy of the + * persistent data table*/ + + __le8 kdf_type; /* The key derivation function used. */ + + /* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */ + __le8 N_factor; /* (1 << N) */ + __le8 r_factor; /* (1 << r) */ + __le8 p_factor; /* (1 << p) */ + __le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and + we have to stop (e.g. power low) this is the last + encrypted 512 byte sector.*/ + __le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS + set, hash of first block, used + to validate before continuing*/ + + /* key_master key, used to sign the derived key which is then used to generate + * the intermediate key + * This key should be used for no other purposes! We use this key to sign unpadded + * data, which is acceptable but only if the key is not reused elsewhere. */ + __le8 keymaster_blob[KEYMASTER_BLOB_SIZE]; + __le32 keymaster_blob_size; + + /* Store scrypt of salted intermediate key. When decryption fails, we can + check if this matches, and if it does, we know that the problem is with the + drive, and there is no point in asking the user for more passwords. + + Note that if any part of this structure is corrupt, this will not match and + we will continue to believe the user entered the wrong password. In that + case the only solution is for the user to enter a password enough times to + force a wipe. + + Note also that there is no need to worry about migration. If this data is + wrong, we simply won't recognise a right password, and will continue to + prompt. On the first password change, this value will be populated and + then we will be OK. + */ + unsigned char scrypted_intermediate_key[SCRYPT_LEN]; + + /* sha of this structure with this element set to zero + Used when encrypting on reboot to validate structure before doing something + fatal + */ + unsigned char sha256[SHA256_DIGEST_LENGTH]; +}; + +/* Persistant data that should be available before decryption. + * Things like airplane mode, locale and timezone are kept + * here and can be retrieved by the CryptKeeper UI to properly + * configure the phone before asking for the password + * This is only valid if the major and minor version above + * is set to 1.1 or higher. + * + * This is a 4K structure. There are 2 copies, and the code alternates + * writing one and then clearing the previous one. The reading + * code reads the first valid copy it finds, based on the magic number. + * The absolute offset to the first of the two copies is kept in rev 1.1 + * and higher crypt_mnt_ftr structures. + */ +struct crypt_persist_entry { + char key[PROPERTY_KEY_MAX]; + char val[PROPERTY_VALUE_MAX]; +}; + +/* Should be exactly 4K in size */ +struct crypt_persist_data { + __le32 persist_magic; + __le32 persist_valid_entries; + __le32 persist_spare[30]; + struct crypt_persist_entry persist_entry[0]; +}; + +#define DATA_MNT_POINT "/data" + +/* Return values for cryptfs_crypto_complete */ +#define CRYPTO_COMPLETE_NOT_ENCRYPTED 1 +#define CRYPTO_COMPLETE_ENCRYPTED 0 +#define CRYPTO_COMPLETE_BAD_METADATA (-1) +#define CRYPTO_COMPLETE_PARTIAL (-2) +#define CRYPTO_COMPLETE_INCONSISTENT (-3) +#define CRYPTO_COMPLETE_CORRUPT (-4) + +/* Return values for cryptfs_enable_inplace*() */ +#define ENABLE_INPLACE_OK 0 +#define ENABLE_INPLACE_ERR_OTHER (-1) +#define ENABLE_INPLACE_ERR_DEV (-2) /* crypto_blkdev issue */ + +/* Return values for cryptfs_getfield */ +#define CRYPTO_GETFIELD_OK 0 +#define CRYPTO_GETFIELD_ERROR_NO_FIELD (-1) +#define CRYPTO_GETFIELD_ERROR_OTHER (-2) +#define CRYPTO_GETFIELD_ERROR_BUF_TOO_SMALL (-3) + +/* Return values for cryptfs_setfield */ +#define CRYPTO_SETFIELD_OK 0 +#define CRYPTO_SETFIELD_ERROR_OTHER (-1) +#define CRYPTO_SETFIELD_ERROR_FIELD_TOO_LONG (-2) +#define CRYPTO_SETFIELD_ERROR_VALUE_TOO_LONG (-3) + +/* Return values for persist_del_key */ +#define PERSIST_DEL_KEY_OK 0 +#define PERSIST_DEL_KEY_ERROR_OTHER (-1) +#define PERSIST_DEL_KEY_ERROR_NO_FIELD (-2) + +int match_multi_entry(const char* key, const char* field, unsigned index); +int wait_and_unmount(const char* mountpoint, bool kill); + +typedef int (*kdf_func)(const char* passwd, const unsigned char* salt, unsigned char* ikey, + void* params); + +int cryptfs_crypto_complete(void); +int cryptfs_check_passwd(const char* pw); +int cryptfs_verify_passwd(const char* pw); +int cryptfs_restart(void); +int cryptfs_enable(int type, const char* passwd, int no_ui); +int cryptfs_changepw(int type, const char* newpw); +int cryptfs_enable_default(int no_ui); +int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev, const unsigned char* key, + char* out_crypto_blkdev); +int cryptfs_revert_ext_volume(const char* label); +int cryptfs_getfield(const char* fieldname, char* value, int len); +int cryptfs_setfield(const char* fieldname, const char* value); +int cryptfs_mount_default_encrypted(void); +int cryptfs_get_password_type(void); +const char* cryptfs_get_password(void); +void cryptfs_clear_password(void); +int cryptfs_isConvertibleToFBE(void); + +uint32_t cryptfs_get_keysize(); +const char* cryptfs_get_crypto_name(); + +#endif /* ANDROID_VOLD_CRYPTFS_H */ diff --git a/crypto/fscrypt/fscrypt_policy.cpp b/crypto/fscrypt/fscrypt_policy.cpp new file mode 100755 index 00000000..43d95522 --- /dev/null +++ b/crypto/fscrypt/fscrypt_policy.cpp @@ -0,0 +1,389 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "fscrypt_policy.h" + +static int encryption_mode = FS_ENCRYPTION_MODE_PRIVATE; + +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 argv = {"ls", "-laZ", dirname}; + int status = 0; + auto res = + android_fork_execvp(argv.size(), const_cast(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; + } +} + +extern "C" void policy_to_hex(const uint8_t* 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(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; + } 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); + } + // 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, uint8_t *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, uint8_t *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, uint8_t *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; + } + uint8_t 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, uint8_t *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; + } + + 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; + } + + 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; +} + +extern "C" bool fscrypt_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); + + std::string contents_encryption_mode_string = std::string(contents_encryption_mode); + int pos = contents_encryption_mode_string.find(":"); + PLOG(ERROR) << "contents_encryption_mode_string: " << contents_encryption_mode_string.substr(0, pos); + + // if (!strcmp(contents_encryption_mode, "software")) { + if (contents_encryption_mode_string.substr(0, pos) == "software") { + encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS; + // } else if (!strcmp(contents_encryption_mode, "ice")) { + } else if (contents_encryption_mode_string.substr(0, pos) == "ice") { + encryption_mode = FS_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; +} + +extern "C" void fscrypt_policy_fill_default_struct(fscrypt_encryption_policy *fep) { + fep->version = 0; + fep->contents_encryption_mode = encryption_mode; + fep->filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS; + fep->flags = 0; + memset((void*)&fep->master_key_descriptor[0], 0, FS_KEY_DESCRIPTOR_SIZE); +} + +extern "C" bool fscrypt_policy_set_struct(const char *directory, const fscrypt_encryption_policy *fep) { + 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; + } + if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, fep)) { + printf("failed to set policy for '%s'\n", directory); + PLOG(ERROR) << "Failed to set encryption policy for " << directory; + close(fd); + return false; + } + close(fd); + return true; +} + +extern "C" bool fscrypt_policy_get_struct(const char *directory, fscrypt_encryption_policy *fep) { + 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; + } + memset(fep, 0, sizeof(fscrypt_encryption_policy)); + if (ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, fep) != 0) { + PLOG(ERROR) << "Failed to get encryption policy for " << directory; + close(fd); + return false; + } + printf("fscrypt_policy_get_struct::fep->version::%d\n", fep->version); + close(fd); + return true; +} + +extern "C" bool fscrypt_policy_set(const char *directory, uint8_t *policy, + size_t policy_length, int contents_encryption_mode) { + if (contents_encryption_mode == 0) + contents_encryption_mode = encryption_mode; + if (policy_length != FS_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; + } + + fscrypt_encryption_policy fep; + fep.version = 0; + fep.contents_encryption_mode = contents_encryption_mode; + fep.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS; + fep.flags = 0; + memcpy(fep.master_key_descriptor, policy, FS_KEY_DESCRIPTOR_SIZE); + if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, &fep)) { + 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[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(policy, policy_hex); + LOG(INFO) << "Policy for " << directory << " set to " << policy_hex; + return true; +} diff --git a/crypto/fscrypt/fscrypt_policy.h b/crypto/fscrypt/fscrypt_policy.h new file mode 100755 index 00000000..01fc4190 --- /dev/null +++ b/crypto/fscrypt/fscrypt_policy.h @@ -0,0 +1,63 @@ +/* + * 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 _FS_CRYPT_H_ +#define _FS_CRYPT_H_ + +#include +#include +#include +#include + +__BEGIN_DECLS + +#define FS_KEY_DESCRIPTOR_SIZE_HEX (2 * FS_KEY_DESCRIPTOR_SIZE + 1) + +/* modes not supported by upstream kernel, so not in */ +#define FS_ENCRYPTION_MODE_AES_256_HEH 126 +#define FS_ENCRYPTION_MODE_PRIVATE 127 + +/* new definition, not yet in Bionic's */ +#ifndef FS_ENCRYPTION_MODE_ADIANTUM +#define FS_ENCRYPTION_MODE_ADIANTUM 9 +#endif + +/* new definition, not yet in Bionic's */ +#ifndef FS_POLICY_FLAG_DIRECT_KEY +#define FS_POLICY_FLAG_DIRECT_KEY 0x4 +#endif + +#define HEX_LOOKUP "0123456789abcdef" + +struct fscrypt_encryption_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)); + + +bool fscrypt_set_mode(); +bool lookup_ref_key(const uint8_t *policy, uint8_t* policy_type); +bool lookup_ref_tar(const uint8_t *policy_type, uint8_t *policy); +void policy_to_hex(const uint8_t* policy, char* hex); +bool fscrypt_policy_get_struct(const char *directory, struct fscrypt_encryption_policy *fep); +bool fscrypt_policy_set_struct(const char *directory, const struct fscrypt_encryption_policy *fep); +void fscrypt_policy_fill_default_struct(struct fscrypt_encryption_policy *fep); +__END_DECLS + +#endif // _FS_CRYPT_H_ diff --git a/crypto/fscrypt/fscryptpolicyget.cpp b/crypto/fscrypt/fscryptpolicyget.cpp new file mode 100755 index 00000000..5add6163 --- /dev/null +++ b/crypto/fscrypt/fscryptpolicyget.cpp @@ -0,0 +1,39 @@ +/* + * 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 "fscrypt_policy.h" + +#define FS_KEY_DESCRIPTOR_SIZE 8 + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("Must specify a path\n"); + return -1; + } else { + fscrypt_encryption_policy fep; + if (fscrypt_policy_get_struct(argv[1], &fep)) { + char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(fep.master_key_descriptor, policy_hex); + printf("%s\n", policy_hex); + } else { + printf("No policy set\n"); + } + } + return 0; +} diff --git a/crypto/fscrypt/keystore_auth.cpp b/crypto/fscrypt/keystore_auth.cpp new file mode 100755 index 00000000..a65fe21b --- /dev/null +++ b/crypto/fscrypt/keystore_auth.cpp @@ -0,0 +1,108 @@ +/* + Copyright 2020 TeamWin + This file is part of TWRP/TeamWin Recovery Project. + + TWRP is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + TWRP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TWRP. If not, see . +*/ + +/* The keystore refuses to allow the root user to supply auth tokens, so + * we write the auth token to a file in TWRP and run a separate service + * (this) that runs as the system user to add the auth token. TWRP waits + * for /auth_token to be deleted and also looks for /auth_error to check + * for errors. TWRP will error out after a while if /auth_token does not + * get deleted. */ + +#include +#include + +#ifdef USE_SECURITY_NAMESPACE +#include +#else +#include +#include +#endif +#include +#include + +#include + +#ifndef LOG_TAG +#define LOG_TAG "keystore_auth" +#endif + +using namespace android; +using android::security::keystore::IKeystoreService; + +void create_error_file() { + FILE* error_file = fopen("/auth_error", "wb"); + if (error_file == NULL) { + printf("Failed to open /auth_error\n"); + ALOGE("Failed to open /auth_error\n"); + return; + } + fwrite("1", 1, 1, error_file); + fclose(error_file); + unlink("/auth_token"); +} + +int main() { + unlink("/auth_error"); + FILE* auth_file = fopen("/auth_token", "rb"); + if (auth_file == NULL) { + printf("Failed to open /auth_token\n"); + ALOGE("Failed to open /auth_token\n"); + create_error_file(); + return -1; + } + // Get the file size + fseek(auth_file, 0, SEEK_END); + int size = ftell(auth_file); + fseek(auth_file, 0, SEEK_SET); + uint8_t auth_token[size]; + fread(auth_token , sizeof(uint8_t), size, auth_file); + fclose(auth_file); + // First get the keystore service + sp sm = defaultServiceManager(); + sp binder = sm->getService(String16("android.security.keystore")); +#ifdef USE_SECURITY_NAMESPACE + sp service = interface_cast(binder); +#else + sp service = interface_cast(binder); +#endif + if (service == NULL) { + printf("error: could not connect to keystore service\n"); + ALOGE("error: could not connect to keystore service\n"); + create_error_file(); + return -2; + } +#ifdef USE_SECURITY_NAMESPACE + std::vector auth_token_vector(&auth_token[0], (&auth_token[0]) + size); + int result = 0; + auto binder_result = service->addAuthToken(auth_token_vector, &result); + if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) { +#else + ::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, size); + if (!auth_result.isOk()) { +#endif + // The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0 + printf("keystore error adding auth token\n"); + ALOGE("keystore error adding auth token\n"); + create_error_file(); + return -3; + } + printf("successfully added auth token to keystore\n"); + ALOGD("successfully added auth token to keystore\n"); + unlink("/auth_token"); + return 0; +} diff --git a/crypto/fscrypt/main.cpp b/crypto/fscrypt/main.cpp new file mode 100644 index 00000000..f0266ae1 --- /dev/null +++ b/crypto/fscrypt/main.cpp @@ -0,0 +1,36 @@ +/* + * 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 "Decrypt.h" + +int main(int argc, char *argv[]) { + bool ret = false; + if (argc < 2) { + Decrypt_DE(); + ret = Decrypt_User(0, "0000"); + } else if (argc < 3) { + Decrypt_DE(); + ret = Decrypt_User(0, argv[1]); + } else { + ret = Decrypt_User(atoi(argv[1]), argv[2]); + } + if (!ret) + printf("Failed to decrypt\n"); + return 0; +} diff --git a/crypto/fscrypt/sehandle.h b/crypto/fscrypt/sehandle.h new file mode 100644 index 00000000..8921db5b --- /dev/null +++ b/crypto/fscrypt/sehandle.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 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 _SEHANDLE_H +#define _SEHANDLE_H + +#include + +extern struct selabel_handle* sehandle; + +#endif diff --git a/data.cpp b/data.cpp index abba1496..0124c8ff 100755 --- a/data.cpp +++ b/data.cpp @@ -1115,13 +1115,7 @@ void DataManager::Output_Version(void) } } } - if (!TWFunc::Path_Exists(recoveryCacheDir)) { - LOGINFO("Recreating %s folder.\n", recoveryCacheDir.c_str()); - if (!TWFunc::Create_Dir_Recursive(recoveryCacheDir.c_str(), S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP, 0, 0)) { - LOGERR("DataManager::Output_Version -- Unable to make %s: %s\n", recoveryCacheDir.c_str(), strerror(errno)); - return; - } - } + std::string verPath = recoveryCacheDir + ".version"; if (TWFunc::Path_Exists(verPath)) { unlink(verPath.c_str()); diff --git a/gui/Android.mk b/gui/Android.mk index 0c7a02c4..33b9020c 100755 --- a/gui/Android.mk +++ b/gui/Android.mk @@ -43,6 +43,9 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/../otautil/include ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) LOCAL_SHARED_LIBRARIES += libziparchive LOCAL_STATIC_LIBRARIES += libotautil + ifneq ($(TW_INCLUDE_CRYPTO),) + LOCAL_C_INCLUDES += bootable/recovery/crypto/fscrypt + endif ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 28; echo $$?),0) LOCAL_C_INCLUDES += $(LOCAL_PATH)/../install/include \ system/core/libziparchive/include/ \ diff --git a/gui/gui.cpp b/gui/gui.cpp index c91dd43c..6d1a0c0e 100755 --- a/gui/gui.cpp +++ b/gui/gui.cpp @@ -774,7 +774,6 @@ extern "C" int gui_loadResources(void) #ifndef TW_OEM_BUILD int check = 0; DataManager::GetValue(TW_IS_ENCRYPTED, check); - if (check) { if (PageManager::LoadPackage("TWRP", TWRES "ui.xml", "decrypt")) diff --git a/gui/partitionlist.cpp b/gui/partitionlist.cpp index 570b16aa..a5a68e0e 100755 --- a/gui/partitionlist.cpp +++ b/gui/partitionlist.cpp @@ -1,5 +1,5 @@ /* - Copyright 2013 bigbiff/Dees_Troy TeamWin + Copyright 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify @@ -261,7 +261,6 @@ void GUIPartitionList::NotifySelect(size_t item_selected) } mList.at(item_selected).selected = 1; mUpdate = 1; - DataManager::SetValue(mVariable, str); } } else { diff --git a/gui/theme/common/languages/en.xml b/gui/theme/common/languages/en.xml index 478f88d6..7982a3b2 100755 --- a/gui/theme/common/languages/en.xml +++ b/gui/theme/common/languages/en.xml @@ -726,5 +726,6 @@ To flash additional zips, please reboot recovery to switch to the updated slot. Starting Ozip Decryption... Ozip Decryption Finished! + WARNING: {1} wiped. FBE device should be booted into Android and not Recovery to set initial FBE policy after wipe. diff --git a/libtar/Android.mk b/libtar/Android.mk old mode 100644 new mode 100755 index 6b464f30..9a35c7ba --- a/libtar/Android.mk +++ b/libtar/Android.mk @@ -14,9 +14,15 @@ LOCAL_C_INCLUDES += external/libselinux/include LOCAL_SHARED_LIBRARIES += libselinux ifeq ($(TW_INCLUDE_CRYPTO_FBE), true) - LOCAL_SHARED_LIBRARIES += libe4crypt - LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT - LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) + LOCAL_SHARED_LIBRARIES += libtwrpfscrypt + LOCAL_CFLAGS += -DUSE_FSCRYPT + LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/fscrypt + else + LOCAL_SHARED_LIBRARIES += libe4crypt + LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT + LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt + endif endif include $(BUILD_SHARED_LIBRARY) @@ -35,9 +41,15 @@ LOCAL_C_INCLUDES += external/libselinux/include LOCAL_STATIC_LIBRARIES += libselinux ifeq ($(TW_INCLUDE_CRYPTO_FBE), true) - LOCAL_SHARED_LIBRARIES += libe4crypt - LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT - LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) + LOCAL_SHARED_LIBRARIES += libtwrpfscrypt + LOCAL_CFLAGS += -DUSE_FSCRYPT + LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/fscrypt + else + LOCAL_SHARED_LIBRARIES += libe4crypt + LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT + LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt + endif endif include $(BUILD_STATIC_LIBRARY) diff --git a/libtar/append.c b/libtar/append.c old mode 100644 new mode 100755 index 8f09de26..3075a61a --- a/libtar/append.c +++ b/libtar/append.c @@ -24,22 +24,28 @@ #include #include +#include #include #ifdef STDC_HEADERS -# include -# include +#include +#include #endif #ifdef HAVE_UNISTD_H -# include +#include #endif #include #ifdef HAVE_EXT4_CRYPT -# include "ext4crypt_tar.h" +#include "ext4crypt_tar.h" #endif + +#ifdef USE_FSCRYPT +#include "fscrypt_policy.h" +#endif + #include "android_utils.h" struct tar_dev @@ -142,6 +148,7 @@ tar_append_file(TAR *t, const char *realname, const char *savename) printf("malloc ext4_encryption_policy\n"); return -1; } + if (e4crypt_policy_get_struct(realname, t->th_buf.eep)) { char tar_policy[EXT4_KEY_DESCRIPTOR_SIZE]; @@ -166,6 +173,43 @@ tar_append_file(TAR *t, const char *realname, const char *savename) } } #endif +#ifdef USE_FSCRYPT + if (TH_ISDIR(t) && t->options & TAR_STORE_FSCRYPT_POL) + { + if (t->th_buf.fep != NULL) + { + free(t->th_buf.fep); + t->th_buf.fep = NULL; + } + + t->th_buf.fep = (struct fscrypt_encryption_policy*)malloc(sizeof(struct fscrypt_encryption_policy)); + if (!t->th_buf.fep) { + printf("malloc fs_encryption_policy\n"); + return -1; + } + + if (fscrypt_policy_get_struct(realname, t->th_buf.fep)) { + uint8_t tar_policy[FS_KEY_DESCRIPTOR_SIZE]; + memset(tar_policy, 0, sizeof(tar_policy)); + char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(t->th_buf.fep->master_key_descriptor, policy_hex); + if (lookup_ref_key(t->th_buf.fep->master_key_descriptor, &tar_policy[0])) { + printf("found fscrypt policy '%s' - '%s' - '%s'\n", realname, tar_policy, policy_hex); + memcpy(t->th_buf.fep->master_key_descriptor, tar_policy, FS_KEY_DESCRIPTOR_SIZE); + } else { + printf("failed to lookup fscrypt tar policy for '%s' - '%s'\n", realname, policy_hex); + free(t->th_buf.fep); + t->th_buf.fep = NULL; + return -1; + } + } + else { + // no policy found, but this is not an error as not all dirs will have a policy + free(t->th_buf.fep); + t->th_buf.fep = NULL; + } + } +#endif /* get posix file capabilities */ if (TH_ISREG(t) && t->options & TAR_STORE_POSIX_CAP) diff --git a/libtar/block.c b/libtar/block.c old mode 100644 new mode 100755 index 834c164a..2f1004ba --- a/libtar/block.c +++ b/libtar/block.c @@ -18,8 +18,14 @@ # include #endif +#define DEBUG 1 + #ifdef HAVE_EXT4_CRYPT -# include "ext4crypt_tar.h" +#include "ext4crypt_tar.h" +#endif + +#ifdef USE_FSCRYPT +#include "fscrypt_policy.h" #endif #define BIT_ISSET(bitmask, bit) ((bitmask) & (bit)) @@ -33,6 +39,10 @@ #define E4CRYPT_TAG "TWRP.security.e4crypt=" #define E4CRYPT_TAG_LEN strlen(E4CRYPT_TAG) +// Used to identify fscrypt_policy in extended ('x') +#define FSCRYPT_TAG "TWRP.security.fscrypt=" +#define FSCRYPT_TAG_LEN strlen(FSCRYPT_TAG) + // Used to identify Posix capabilities in extended ('x') #define CAPABILITIES_TAG "SCHILY.xattr.security.capability=" #define CAPABILITIES_TAG_LEN strlen(CAPABILITIES_TAG) @@ -132,7 +142,7 @@ th_read(TAR *t) char *ptr; #ifdef DEBUG - printf("==> th_read(t=0x%lx)\n", t); + printf("==> th_read(t=0x%p)\n", (void *)t); #endif if (t->th_buf.gnu_longname != NULL) @@ -145,6 +155,12 @@ th_read(TAR *t) if (t->th_buf.eep != NULL) free(t->th_buf.eep); #endif + +#ifdef USE_FSCRYPT + if (t->th_buf.fep != NULL) + free(t->th_buf.fep); +#endif + if (t->th_buf.has_cap_data) { memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data)); @@ -178,7 +194,7 @@ th_read(TAR *t) } #ifdef DEBUG printf(" th_read(): GNU long linkname detected " - "(%ld bytes, %d blocks)\n", sz, blocks); + "(%zu bytes, %zu blocks)\n", sz, blocks); #endif t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE); if (t->th_buf.gnu_longlink == NULL) @@ -189,7 +205,7 @@ th_read(TAR *t) { #ifdef DEBUG printf(" th_read(): reading long linkname " - "(%d blocks left, ptr == %ld)\n", blocks-j, ptr); + "(%zu blocks left, ptr == %p)\n", blocks-j, (void *) ptr); #endif i = tar_block_read(t, ptr); if (i != T_BLOCKSIZE) @@ -228,7 +244,7 @@ th_read(TAR *t) } #ifdef DEBUG printf(" th_read(): GNU long filename detected " - "(%ld bytes, %d blocks)\n", sz, blocks); + "(%zu bytes, %zu blocks)\n", sz, blocks); #endif t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE); if (t->th_buf.gnu_longname == NULL) @@ -239,7 +255,7 @@ th_read(TAR *t) { #ifdef DEBUG printf(" th_read(): reading long filename " - "(%d blocks left, ptr == %ld)\n", blocks-j, ptr); + "(%zu blocks left, ptr == %p)\n", blocks-j, (void *) ptr); #endif i = tar_block_read(t, ptr); if (i != T_BLOCKSIZE) @@ -266,7 +282,7 @@ th_read(TAR *t) } } - // Extended headers (selinux contexts, posix file capabilities, ext4 encryption policies) + // Extended headers (selinux contexts, posix file capabilities and encryption policies) while(TH_ISEXTHEADER(t) || TH_ISPOLHEADER(t)) { sz = th_get_size(t); @@ -386,6 +402,36 @@ th_read(TAR *t) } } #endif // HAVE_EXT4_CRYPT + +#ifdef USE_FSCRYPT + start = strstr(buf, FSCRYPT_TAG); + if (start && start+FSCRYPT_TAG_LEN < buf+len) { + t->th_buf.fep = (struct fscrypt_encryption_policy*)malloc(sizeof(struct fscrypt_encryption_policy)); + if (!t->th_buf.fep) { + printf("malloc fscrypt_encryption_policy\n"); + return -1; + } + start += FSCRYPT_TAG_LEN; + if (*start == '0') { + start++; + char *newline_check = start + sizeof(struct fscrypt_encryption_policy); + if (*newline_check != '\n') + printf("did not find newline char in expected location, continuing anyway...\n"); + memcpy(t->th_buf.fep, start, sizeof(struct fscrypt_encryption_policy)); +#ifdef DEBUG + printf(" th_read(): FSCrypt policy v1 detected: %i %i %i %i %s\n", + (int)t->th_buf.fep->version, + (int)t->th_buf.fep->contents_encryption_mode, + (int)t->th_buf.fep->filenames_encryption_mode, + (int)t->th_buf.fep->flags, + t->th_buf.fep->master_key_descriptor); +#endif + } + else { + printf(" invalid fscrypt header found\n"); + } + } +#endif // USE_FSCRYPT } i = th_read_internal(t); @@ -616,6 +662,38 @@ th_write(TAR *t) } #endif +#ifdef USE_FSCRYPT + if((t->options & TAR_STORE_FSCRYPT_POL) && t->th_buf.fep != NULL) + { +#ifdef DEBUG + printf("th_write(): using fscrypt_policy %s\n", + t->th_buf.fep->master_key_descriptor); +#endif + /* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *version code* *content* *newline* */ + // size newline + sz = FSCRYPT_TAG_LEN + sizeof(struct fscrypt_encryption_policy) + 1 + 3 + 1; + + if(sz >= 100) // another ascci digit for size + ++sz; + + if (total_sz + sz >= T_BLOCKSIZE) + { + if (th_write_extended(t, &buf[0], total_sz)) + return -1; + ptr = buf; + total_sz = sz; + } + else + total_sz += sz; + + snprintf(ptr, T_BLOCKSIZE, "%d "FSCRYPT_TAG"0", (int)sz); + memcpy(ptr + sz - sizeof(struct fscrypt_encryption_policy) - 1, t->th_buf.fep, sizeof(struct fscrypt_encryption_policy)); + char *nlptr = ptr + sz - 1; + *nlptr = '\n'; + ptr += sz; + } +#endif + if((t->options & TAR_STORE_POSIX_CAP) && t->th_buf.has_cap_data) { #ifdef DEBUG diff --git a/libtar/extract.c b/libtar/extract.c old mode 100644 new mode 100755 index ea86c233..fd5e3c9f --- a/libtar/extract.c +++ b/libtar/extract.c @@ -36,8 +36,13 @@ #include #ifdef HAVE_EXT4_CRYPT -# include "ext4crypt_tar.h" +#include "ext4crypt_tar.h" #endif + +#ifdef USE_FSCRYPT +#include "fscrypt_policy.h" +#endif + #include "android_utils.h" const unsigned long long progress_size = (unsigned long long)(T_BLOCKSIZE); @@ -572,6 +577,31 @@ tar_extract_dir(TAR *t, const char *realname) } #endif +#ifdef USE_FSCRYPT + if(t->th_buf.fep != NULL) + { +#ifdef DEBUG + printf("tar_extract_file(): restoring fscrypt policy %s to dir %s\n", t->th_buf.fep->master_key_descriptor, realname); +#endif + uint8_t binary_policy[FS_KEY_DESCRIPTOR_SIZE]; + if (!lookup_ref_tar(t->th_buf.fep->master_key_descriptor, &binary_policy[0])) { + printf("error looking up proper fscrypt policy for '%s' - %s\n", realname, t->th_buf.fep->master_key_descriptor); + return -1; + } + char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(binary_policy, policy_hex); + printf("restoring policy %s > '%s' to '%s'\n", t->th_buf.fep->master_key_descriptor, policy_hex, realname); + memcpy(&t->th_buf.fep->master_key_descriptor, binary_policy, FS_KEY_DESCRIPTOR_SIZE); + if (!fscrypt_policy_set_struct(realname, t->th_buf.fep)) + { + printf("tar_extract_file(): failed to restore fscrypt policy to dir '%s' '%s'!!!\n", realname, policy_hex); + //return -1; // This may not be an error in some cases, so log and ignore + } + } + else + printf("NULL FSCRYPT\n"); +#endif + return 0; } diff --git a/libtar/libtar.h b/libtar/libtar.h old mode 100644 new mode 100755 index aa637b13..19ddd06b --- a/libtar/libtar.h +++ b/libtar/libtar.h @@ -24,6 +24,10 @@ # include "ext4crypt_tar.h" #endif +#ifdef USE_FSCRYPT +#include "fscrypt_policy.h" +#endif + #ifdef __cplusplus extern "C" { @@ -70,6 +74,9 @@ struct tar_header char *selinux_context; #ifdef HAVE_EXT4_CRYPT struct ext4_encryption_policy *eep; +#endif +#ifdef USE_FSCRYPT + struct fscrypt_encryption_policy *fep; #endif int has_cap_data; struct vfs_cap_data cap_data; @@ -120,7 +127,12 @@ 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 */ +#ifdef HAVE_EXT4_CRYPT #define TAR_STORE_EXT4_POL 512 /* store ext4 crypto policy */ +#endif +#ifdef USE_FSCRYPT +#define TAR_STORE_FSCRYPT_POL 512 /* store fscrypt crypto policy */ +#endif #define TAR_STORE_POSIX_CAP 1024 /* store posix file capabilities */ #define TAR_STORE_ANDROID_USER_XATTR 2048 /* store android user.* xattr */ diff --git a/libtar/output.c b/libtar/output.c old mode 100644 new mode 100755 index f5431b6a..68c3e5f2 --- a/libtar/output.c +++ b/libtar/output.c @@ -25,9 +25,12 @@ #endif #ifdef HAVE_EXT4_CRYPT -# include "ext4crypt_tar.h" +#include "ext4crypt_tar.h" #endif +#ifdef USE_FSCRYPT +#include "fscrypt_policy.h" +#endif #ifndef _POSIX_LOGIN_NAME_MAX # define _POSIX_LOGIN_NAME_MAX 9 @@ -65,6 +68,10 @@ th_print(TAR *t) printf(" eep = \"%s\"\n", (t->th_buf.eep ? t->th_buf.eep->master_key_descriptor : "[NULL]")); #endif +#ifdef USE_FSCRYPT + printf(" fep = \"%s\"\n", + (t->th_buf.fep ? t->th_buf.fep->master_key_descriptor : (uint8_t*) "[NULL]")); +#endif } diff --git a/partition.cpp b/partition.cpp index 2d619642..183283dc 100755 --- a/partition.cpp +++ b/partition.cpp @@ -1,5 +1,5 @@ /* - Copyright 2013 to 2017 TeamWin + Copyright 2013 to 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify @@ -19,17 +19,21 @@ #include #include #include -#include -#include + #include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include #include "cutils/properties.h" #include "libblkid/include/blkid.h" @@ -138,7 +142,6 @@ enum TW_FSTAB_FLAGS { TWFLAG_LENGTH, TWFLAG_MOUNTTODECRYPT, TWFLAG_REMOVABLE, - TWFLAG_RETAINLAYOUTVERSION, TWFLAG_SETTINGSSTORAGE, TWFLAG_STORAGE, TWFLAG_STORAGENAME, @@ -183,7 +186,6 @@ const struct flag_list tw_flags[] = { { "length=", TWFLAG_LENGTH }, { "mounttodecrypt", TWFLAG_MOUNTTODECRYPT }, { "removable", TWFLAG_REMOVABLE }, - { "retainlayoutversion", TWFLAG_RETAINLAYOUTVERSION }, { "settingsstorage", TWFLAG_SETTINGSSTORAGE }, { "storage", TWFLAG_STORAGE }, { "storagename=", TWFLAG_STORAGENAME }, @@ -259,7 +261,6 @@ TWPartition::TWPartition() { Mount_Options = ""; Format_Block_Size = 0; Ignore_Blkid = false; - Retain_Layout_Version = false; Crypto_Key_Location = ""; MTP_Storage_ID = 0; Can_Flash_Img = false; @@ -665,16 +666,10 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) { char crypto_blkdev[255]; property_get("ro.crypto.fs_crypto_blkdev", crypto_blkdev, "error"); if (strcmp(crypto_blkdev, "error") != 0) { - DataManager::SetValue(TW_IS_DECRYPTED, 1); - Is_Encrypted = true; - Is_Decrypted = true; - if (Key_Directory.empty()) - Is_FBE = false; - else - Is_FBE = true; - DataManager::SetValue(TW_IS_FBE, 0); + Set_FBE_Status(); Decrypted_Block_Device = crypto_blkdev; LOGINFO("Data already decrypted, new block device: '%s'\n", crypto_blkdev); + DataManager::SetValue(TW_IS_ENCRYPTED, 0); } else if (!Mount(false)) { if (Is_Present) { if (Key_Directory.empty()) { @@ -685,7 +680,6 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) { Can_Be_Mounted = false; Current_File_System = "emmc"; Setup_Image(); - DataManager::SetValue(TW_IS_ENCRYPTED, 1); DataManager::SetValue(TW_CRYPTO_PWTYPE, cryptfs_get_password_type()); DataManager::SetValue(TW_CRYPTO_PASSWORD, ""); DataManager::SetValue("tw_crypto_display", ""); @@ -700,7 +694,7 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) { LOGERR("Primary block device '%s' for mount point '%s' is not present!\n", Primary_Block_Device.c_str(), Mount_Point.c_str()); } } else { - + Set_FBE_Status(); if (!Decrypt_FBE_DE()) { char wrappedvalue[PROPERTY_VALUE_MAX]; property_get("fbe.data.wrappedkey", wrappedvalue, ""); @@ -715,10 +709,12 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) { } } } + DataManager::SetValue(TW_IS_ENCRYPTED, 0); } if (datamedia && (!Is_Encrypted || (Is_Encrypted && Is_Decrypted))) { Setup_Data_Media(); - Recreate_Media_Folder(); + if (!TWFunc::Is_Mount_Wiped("/data")) + Recreate_Media_Folder(); } #else if (datamedia) { @@ -728,8 +724,22 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) { #endif } +void TWPartition::Set_FBE_Status() { + DataManager::SetValue(TW_IS_DECRYPTED, 1); + Is_Encrypted = true; + Is_Decrypted = true; + LOGINFO("Setup_Data_Partition::Key_Directory::%s\n", Key_Directory.c_str()); + if (Key_Directory.empty()) { + Is_FBE = false; + DataManager::SetValue(TW_IS_FBE, 0); + } else { + Is_FBE = true; + DataManager::SetValue(TW_IS_FBE, 1); + } +} + bool TWPartition::Decrypt_FBE_DE() { -if (TWFunc::Path_Exists("/data/unencrypted/key/version")) { + if (TWFunc::Path_Exists("/data/unencrypted/key/version")) { DataManager::SetValue(TW_IS_FBE, 1); LOGINFO("File Based Encryption is present\n"); #ifdef TW_INCLUDE_FBE @@ -737,6 +747,7 @@ if (TWFunc::Path_Exists("/data/unencrypted/key/version")) { 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 + "/misc/vold/volume_keys"); //ExcludeAll(Mount_Point + "/system_ce"); //ExcludeAll(Mount_Point + "/system_de"); //ExcludeAll(Mount_Point + "/misc_ce"); @@ -752,6 +763,8 @@ if (TWFunc::Path_Exists("/data/unencrypted/key/version")) { ExcludeAll(Mount_Point + "/misc/keystore"); ExcludeAll(Mount_Point + "/drm/kek.dat"); ExcludeAll(Mount_Point + "/system_de/0/spblob"); // contains data needed to decrypt pixel 2 + ExcludeAll(Mount_Point + "/per_boot"); // removed each boot by init + int retry_count = 3; while (!Decrypt_DE() && --retry_count) usleep(2000); @@ -761,7 +774,6 @@ if (TWFunc::Path_Exists("/data/unencrypted/key/version")) { Is_Decrypted = false; Is_FBE = true; DataManager::SetValue(TW_IS_FBE, 1); - DataManager::SetValue(TW_IS_ENCRYPTED, 1); string filename; int pwd_type = Get_Password_Type(0, filename); if (pwd_type < 0) { @@ -936,9 +948,6 @@ void TWPartition::Apply_TW_Flag(const unsigned flag, const char* str, const bool case TWFLAG_REMOVABLE: Removable = val; break; - case TWFLAG_RETAINLAYOUTVERSION: - Retain_Layout_Version = val; - break; case TWFLAG_SETTINGSSTORAGE: Is_Settings_Storage = val; if (Is_Settings_Storage) @@ -1182,7 +1191,6 @@ void TWPartition::Setup_Data_Media() { DataManager::SetValue("tw_has_data_media", 1); backup_exclusions.add_absolute_dir("/data/data/com.google.android.music/files"); wipe_exclusions.add_absolute_dir(Mount_Point + "/misc/vold"); // adopted storage keys - ExcludeAll(Mount_Point + "/.layout_version"); ExcludeAll(Mount_Point + "/system/storage.xml"); } else { if (Mount(true) && TWFunc::Path_Exists(Mount_Point + "/media/0")) { @@ -1583,7 +1591,7 @@ bool TWPartition::Mount(bool Display_Error) { mkdir("/system", 0755); mount("/system_root/system", "/system", "auto", MS_BIND, NULL); } - #endif +#endif return true; } @@ -1650,7 +1658,7 @@ bool TWPartition::ReMount_RW(bool Display_Error) { bool TWPartition::Wipe(string New_File_System) { bool wiped = false, update_crypt = false, recreate_media = true; int check; - string Layout_Filename = Mount_Point + "/.layout_version"; + fscrypt_encryption_policy policy; if (!Can_Be_Wiped) { gui_msg(Msg(msg::kError, "cannot_wipe=Partition {1} cannot be wiped.")(Display_Name)); @@ -1660,13 +1668,13 @@ bool TWPartition::Wipe(string New_File_System) { if (Mount_Point == "/cache") Log_Offset = 0; - if (Retain_Layout_Version && Mount(false) && TWFunc::Path_Exists(Layout_Filename)) - TWFunc::copy_file(Layout_Filename, "/.layout_version", 0600); - else - unlink("/.layout_version"); - if (Has_Data_Media && Current_File_System == New_File_System) { wiped = Wipe_Data_Without_Wiping_Media(); + if (Mount_Point == "/data" && TWFunc::get_cache_dir() == AB_CACHE_DIR && !TWFunc::Is_Mount_Wiped("/data")) { + bool created = Recreate_AB_Cache_Dir(policy); + if (created) + gui_msg(Msg(msg::kWarning, "fbe_wipe_msg=WARNING: {1} wiped. FBE device should be booted into Android and not Recovery to set initial FBE policy after wipe.")(TWFunc::get_cache_dir())); + } recreate_media = false; } else { DataManager::GetValue(TW_RM_RF_VAR, check); @@ -1689,19 +1697,13 @@ bool TWPartition::Wipe(string New_File_System) { wiped = Wipe_NTFS(); else { LOGERR("Unable to wipe '%s' -- unknown file system '%s'\n", Mount_Point.c_str(), New_File_System.c_str()); - unlink("/.layout_version"); return false; } update_crypt = wiped; + update_crypt = false; } if (wiped) { - if (Mount_Point == "/cache") - DataManager::Output_Version(); - - if (TWFunc::Path_Exists("/.layout_version") && Mount(false)) - TWFunc::copy_file("/.layout_version", Layout_Filename, 0600); - if (update_crypt) { Setup_File_System(false); if (Is_Encrypted && !Is_Decrypted) { @@ -1716,11 +1718,8 @@ bool TWPartition::Wipe(string New_File_System) { } } - if (Has_Data_Media && recreate_media) { - Recreate_Media_Folder(); - } - if (Is_Storage && Mount(false)) - PartitionManager.Add_MTP_Storage(MTP_Storage_ID); + // if (Is_Storage && Mount(false)) + // PartitionManager.Add_MTP_Storage(MTP_Storage_ID); } return wiped; @@ -2010,10 +2009,10 @@ bool TWPartition::Wipe_Encryption() { gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name)); return false; } - if (!UnMount(true)) - goto exit; #ifdef TW_INCLUDE_CRYPTO + if (!UnMount(true)) + return false; if (Is_Decrypted && !Decrypted_Block_Device.empty()) { if (delete_crypto_blk_dev((char*)("userdata")) != 0) { LOGERR("Error deleting crypto block device, continuing anyway.\n"); @@ -2026,14 +2025,16 @@ bool TWPartition::Wipe_Encryption() { Is_Encrypted = false; if (Wipe(Fstab_File_System)) { Has_Data_Media = Save_Data_Media; - if (Has_Data_Media && !Symlink_Mount_Point.empty()) { - Recreate_Media_Folder(); - if (Mount(false)) - PartitionManager.Add_MTP_Storage(MTP_Storage_ID); - } + // if (Has_Data_Media && !Symlink_Mount_Point.empty()) { + // if (Mount(false)) + // PartitionManager.Add_MTP_Storage(MTP_Storage_ID); + // } DataManager::SetValue(TW_IS_ENCRYPTED, 0); #ifndef TW_OEM_BUILD - gui_msg("format_data_msg=You may need to reboot recovery to be able to use /data again."); + if (Is_FBE) + gui_msg(Msg(msg::kWarning, "fbe_wipe_msg=WARNING: {1} wiped. FBE device should be booted into Android and not Recovery to set initial FBE policy after wipe.")(TWFunc::get_cache_dir())); + else + gui_msg("format_data_msg=You may need to reboot recovery to be able to use /data again."); #endif ret = true; if (!Key_Directory.empty()) @@ -2099,6 +2100,9 @@ void TWPartition::Check_FS_Type() { } bool TWPartition::Wipe_EXTFS(string File_System) { + if (!UnMount(true)) + return false; + #if PLATFORM_SDK_VERSION < 28 if (!TWFunc::Path_Exists("/sbin/mke2fs")) #else @@ -2115,8 +2119,6 @@ bool TWPartition::Wipe_EXTFS(string File_System) { gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name)); return false; } - if (!UnMount(true)) - return false; /** * On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper, @@ -2184,14 +2186,15 @@ bool TWPartition::Wipe_EXT4() { int ret; bool NeedPreserveFooter = true; + if (!UnMount(true)) + return false; + Find_Actual_Block_Device(); if (!Is_Present) { LOGINFO("Block device not present, cannot wipe %s.\n", Display_Name.c_str()); gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name)); return false; } - if (!UnMount(true)) - return false; /** * On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper, @@ -2239,10 +2242,10 @@ bool TWPartition::Wipe_EXT4() { bool TWPartition::Wipe_FAT() { string command; - if (TWFunc::Path_Exists("/sbin/mkfs.fat")) { - if (!UnMount(true)) - return false; + if (!UnMount(true)) + return false; + if (TWFunc::Path_Exists("/sbin/mkfs.fat")) { gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mkfs.fat")); Find_Actual_Block_Device(); command = "mkfs.fat " + Actual_Block_Device; @@ -2266,10 +2269,9 @@ bool TWPartition::Wipe_FAT() { bool TWPartition::Wipe_EXFAT() { string command; + if (!UnMount(true)) + return false; if (TWFunc::Path_Exists("/sbin/mkexfatfs")) { - if (!UnMount(true)) - return false; - gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mkexfatfs")); Find_Actual_Block_Device(); command = "mkexfatfs " + Actual_Block_Device; @@ -2335,92 +2337,73 @@ bool TWPartition::Wipe_RMRF() { } bool TWPartition::Wipe_F2FS() { - string command; + std::string command; + std::string f2fs_bin; - if (TWFunc::Path_Exists("/sbin/mkfs.f2fs")) { - bool NeedPreserveFooter = true; + if (!UnMount(true)) + return false; - Find_Actual_Block_Device(); - if (!Is_Present) { - LOGINFO("Block device not present, cannot wipe %s.\n", Display_Name.c_str()); - gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name)); - return false; - } - if (!UnMount(true)) - return false; - - /** - * On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper, - * so there's no need to preserve footer. - */ - if ((Is_Decrypted && !Decrypted_Block_Device.empty()) || - Crypto_Key_Location != "footer") { - NeedPreserveFooter = false; - } - - gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mkfs.f2fs")); - // First determine if we have the old mkfs.f2fs that uses "-r reserved_bytes" - // or the new mkfs.f2fs that expects the number of sectors as the optional last argument - // Note: some 7.1 trees have the old and some have the new. - command = "mkfs.f2fs | grep \"reserved\" > /tmp/f2fsversiontest"; - TWFunc::Exec_Cmd(command, false); // no help argument so printing usage exits with an error code - if (!TWFunc::Path_Exists("/tmp/f2fsversiontest")) { - LOGINFO("Error determining mkfs.f2fs version\n"); - return false; - } - if (TWFunc::Get_File_Size("/tmp/f2fsversiontest") <= 0) { - LOGINFO("Using newer mkfs.f2fs\n"); - unsigned long long dev_sz = TWFunc::IOCTL_Get_Block_Size(Actual_Block_Device.c_str()); - if (!dev_sz) - return false; - - if (NeedPreserveFooter) - Length < 0 ? dev_sz += Length : dev_sz -= CRYPT_FOOTER_OFFSET; - - char dev_sz_str[48]; - sprintf(dev_sz_str, "%llu", (dev_sz / 4096)); - command = "mkfs.f2fs -d1 -f -O encrypt -O quota -O verity -w 4096 " + Actual_Block_Device + " " + dev_sz_str; - if (TWFunc::Path_Exists("/sbin/sload.f2fs")) { - command += " && sload.f2fs -t /data " + Actual_Block_Device; - } - } else { - LOGINFO("Using older mkfs.f2fs\n"); - command = "mkfs.f2fs -t 0"; - if (NeedPreserveFooter) { - // Only use length if we're not decrypted - char len[32]; - int mod_length = Length; - if (Length < 0) - mod_length *= -1; - sprintf(len, "%i", mod_length); - command += " -r "; - command += len; - } - command += " " + Actual_Block_Device; - } - LOGINFO("mkfs.f2fs command: %s\n", command.c_str()); - if (TWFunc::Exec_Cmd(command) == 0) { - if (NeedPreserveFooter) - Wipe_Crypto_Key(); - Recreate_AndSec_Folder(); - gui_msg("done=Done."); - return true; - } else { - gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name)); - return false; - } - return true; - } else { + if (TWFunc::Path_Exists("/sbin/mkfs.f2fs")) + f2fs_bin = "/sbin/mkfs.f2fs"; + else if (TWFunc::Path_Exists("/sbin/make_f2fs")) + f2fs_bin = "/sbin/make_f2fs"; + else { LOGINFO("mkfs.f2fs binary not found, using rm -rf to wipe.\n"); return Wipe_RMRF(); } - return false; + + bool NeedPreserveFooter = true; + + Find_Actual_Block_Device(); + if (!Is_Present) { + LOGINFO("Block device not present, cannot wipe %s.\n", Display_Name.c_str()); + gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name)); + return false; + } + + unsigned long long dev_sz = TWFunc::IOCTL_Get_Block_Size(Actual_Block_Device.c_str()); + if (!dev_sz) + return false; + + if (NeedPreserveFooter) + Length < 0 ? dev_sz += Length : dev_sz -= CRYPT_FOOTER_OFFSET; + + char dev_sz_str[48]; + sprintf(dev_sz_str, "%llu", (dev_sz / 4096)); + command = f2fs_bin + " -d1 -f -O encrypt -O quota -O verity -w 4096 " + Actual_Block_Device + " " + dev_sz_str; + if (TWFunc::Path_Exists("/sbin/sload.f2fs")) { + command += " && sload.f2fs -t /data " + Actual_Block_Device; + } + + /** + * On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper, + * so there's no need to preserve footer. + */ + if ((Is_Decrypted && !Decrypted_Block_Device.empty()) || + Crypto_Key_Location != "footer") { + NeedPreserveFooter = false; + } + LOGINFO("mkfs.f2fs command: %s\n", f2fs_bin.c_str()); + if (TWFunc::Exec_Cmd(command) == 0) { + if (NeedPreserveFooter) + Wipe_Crypto_Key(); + Recreate_AndSec_Folder(); + gui_msg("done=Done."); + return true; + } else { + gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name)); + return false; + } + return true; } bool TWPartition::Wipe_NTFS() { string command; string Ntfsmake_Binary; + if (!UnMount(true)) + return false; + if (TWFunc::Path_Exists("/sbin/mkntfs")) Ntfsmake_Binary = "mkntfs"; else if (TWFunc::Path_Exists("/sbin/mkfs.ntfs")) @@ -2428,9 +2411,6 @@ bool TWPartition::Wipe_NTFS() { else return false; - if (!UnMount(true)) - return false; - gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)(Ntfsmake_Binary)); Find_Actual_Block_Device(); command = "/sbin/" + Ntfsmake_Binary + " " + Actual_Block_Device; @@ -2463,6 +2443,59 @@ bool TWPartition::Wipe_Data_Without_Wiping_Media() { #endif // ifdef TW_OEM_BUILD } +bool TWPartition::Recreate_AB_Cache_Dir(const fscrypt_encryption_policy &policy) { + struct passwd pd; + struct passwd *pwdptr = &pd; + struct passwd *tempPd; + char pwdBuf[512]; + int uid = 0, gid = 0; + + if ((getpwnam_r("system", pwdptr, pwdBuf, sizeof(pwdBuf), &tempPd)) != 0) { + LOGERR("unable to get system user id\n"); + return false; + } else { + struct group grp; + struct group *grpptr = &grp; + struct group *tempGrp; + char grpBuf[512]; + + if ((getgrnam_r("cache", grpptr, grpBuf, sizeof(grpBuf), &tempGrp)) != 0) { + LOGERR("unable to get cache group id\n"); + return false; + } else { + uid = pd.pw_uid; + gid = grp.gr_gid; + + if (!TWFunc::Create_Dir_Recursive(AB_CACHE_DIR, S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP, uid, gid)) { + LOGERR("Unable to recreate %s\n", AB_CACHE_DIR); + return false; + } + if (setfilecon(AB_CACHE_DIR, "u:object_r:cache_file:s0") != 0) { + LOGERR("Unable to set contexts for %s\n", AB_CACHE_DIR); + return false; + } + char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(policy.master_key_descriptor, policy_hex); + LOGINFO("setting policy for %s: %s\n", policy_hex, AB_CACHE_DIR); + if (sizeof(policy.master_key_descriptor) > 0) { + if (!TWFunc::Set_Encryption_Policy(AB_CACHE_DIR, policy)) { + LOGERR("Unable to set encryption policy for %s\n", AB_CACHE_DIR); + LOGINFO("Removing %s\n", AB_CACHE_DIR); + int ret = TWFunc::removeDir(AB_CACHE_DIR, true); + if (ret == -1) { + LOGERR("Unable to remove %s\n", AB_CACHE_DIR); + } + return false; + } + } else { + LOGERR("Not setting empty policy to %s\n", AB_CACHE_DIR); + return false; + } + } + } + return true; +} + bool TWPartition::Wipe_Data_Without_Wiping_Media_Func(const string& parent __unused) { string dir; @@ -3331,7 +3364,6 @@ int TWPartition::Decrypt_Adopted() { PartitionManager.Remove_Partition_By_Path("/sd-ext"); } Setup_Data_Media(); - Recreate_Media_Folder(); Wipe_Available_in_GUI = true; Wipe_During_Factory_Reset = true; Can_Be_Backed_Up = true; diff --git a/partitionmanager.cpp b/partitionmanager.cpp index e0c40eaa..ae4c70a8 100755 --- a/partitionmanager.cpp +++ b/partitionmanager.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -84,19 +85,23 @@ extern "C" { } #ifdef TW_INCLUDE_CRYPTO - #include "crypto/fde/cryptfs.h" - #include "gui/rapidxml.hpp" - #include "gui/pages.hpp" - #ifdef TW_INCLUDE_FBE - #include "crypto/ext4crypt/Decrypt.h" - #ifdef TW_INCLUDE_FBE_METADATA_DECRYPT - #include "crypto/ext4crypt/MetadataCrypt.h" - #endif - #endif - #ifdef TW_CRYPTO_USE_SYSTEM_VOLD - #include "crypto/vold_decrypt/vold_decrypt.h" +#include "crypto/fde/cryptfs.h" +#include "gui/rapidxml.hpp" +#include "gui/pages.hpp" +#ifdef TW_INCLUDE_FBE +#include "crypto/ext4crypt/Decrypt.h" +#ifdef TW_INCLUDE_FBE_METADATA_DECRYPT + #ifdef USE_FSCRYPT + #include "crypto/fscrypt/MetadataCrypt.h" + #else + #include "crypto/ext4crypt/MetadataCrypt.h" #endif #endif +#endif +#ifdef TW_CRYPTO_USE_SYSTEM_VOLD +#include "crypto/vold_decrypt/vold_decrypt.h" +#endif +#endif #ifdef AB_OTA_UPDATER #include @@ -307,13 +312,59 @@ int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error, if (settings_partition) { Setup_Settings_Storage_Partition(settings_partition); } -#ifdef TW_INCLUDE_CRYPTO + + Update_System_Details(); + UnMount_Main_Partitions(); +#ifdef AB_OTA_UPDATER + DataManager::SetValue("tw_active_slot", Get_Active_Slot_Display()); +#endif + setup_uevent(); + return true; +} + +int TWPartitionManager::Write_Fstab(void) { + FILE *fp; + std::vector::iterator iter; + string Line; + + fp = fopen("/etc/fstab", "w"); + if (fp == NULL) { + LOGINFO("Can not open /etc/fstab.\n"); + return false; + } + for (iter = Partitions.begin(); iter != Partitions.end(); iter++) { + if ((*iter)->Can_Be_Mounted) { + Line = (*iter)->Actual_Block_Device + " " + (*iter)->Mount_Point + " " + (*iter)->Current_File_System + " rw 0 0\n"; + fputs(Line.c_str(), fp); + } + // Handle subpartition tracking + if ((*iter)->Is_SubPartition) { + TWPartition* ParentPartition = Find_Partition_By_Path((*iter)->SubPartition_Of); + if (ParentPartition) + ParentPartition->Has_SubPartition = true; + else + LOGERR("Unable to locate parent partition '%s' of '%s'\n", (*iter)->SubPartition_Of.c_str(), (*iter)->Mount_Point.c_str()); + } + } + fclose(fp); + return true; +} + +void TWPartitionManager::Decrypt_Data() { + #ifdef TW_INCLUDE_CRYPTO TWPartition* Decrypt_Data = Find_Partition_By_Path("/data"); if (Decrypt_Data && Decrypt_Data->Is_Encrypted && !Decrypt_Data->Is_Decrypted) { if (!Decrypt_Data->Key_Directory.empty() && Mount_By_Path(Decrypt_Data->Key_Directory, false)) { #ifdef TW_INCLUDE_FBE_METADATA_DECRYPT +#ifdef USE_FSCRYPT + if (fscrypt_mount_metadata_encrypted(Decrypt_Data->Actual_Block_Device, Decrypt_Data->Mount_Point, false)) { + std::string crypto_blkdev = android::base::GetProperty("ro.crypto.fs_crypto_blkdev", "error"); + LOGINFO("Successfully decrypted metadata encrypted data partition with new block device: '%s'\n", crypto_blkdev.c_str()); +#else if (e4crypt_mount_metadata_encrypted(Decrypt_Data->Mount_Point, false, Decrypt_Data->Key_Directory, Decrypt_Data->Actual_Block_Device, &Decrypt_Data->Decrypted_Block_Device)) { - LOGINFO("Successfully decrypted metadata encrypted data partition with new block device: '%s'\n", Decrypt_Data->Decrypted_Block_Device.c_str()); + LOGINFO("Successfully decrypted metadata encrypted data partition with new block device: '%s'\n", + Decrypt_Data->Decrypted_Block_Device.c_str()); +#endif property_set("ro.crypto.state", "encrypted"); Decrypt_Data->Is_Decrypted = true; // Needed to make the mount function work correctly int retry_count = 10; @@ -373,41 +424,6 @@ int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error, Decrypt_Adopted(); } #endif - Update_System_Details(); - UnMount_Main_Partitions(); -#ifdef AB_OTA_UPDATER - DataManager::SetValue("tw_active_slot", Get_Active_Slot_Display()); -#endif - setup_uevent(); - return true; -} - -int TWPartitionManager::Write_Fstab(void) { - FILE *fp; - std::vector::iterator iter; - string Line; - - fp = fopen("/etc/fstab", "w"); - if (fp == NULL) { - LOGINFO("Can not open /etc/fstab.\n"); - return false; - } - for (iter = Partitions.begin(); iter != Partitions.end(); iter++) { - if ((*iter)->Can_Be_Mounted) { - Line = (*iter)->Actual_Block_Device + " " + (*iter)->Mount_Point + " " + (*iter)->Current_File_System + " rw 0 0\n"; - fputs(Line.c_str(), fp); - } - // Handle subpartition tracking - if ((*iter)->Is_SubPartition) { - TWPartition* ParentPartition = Find_Partition_By_Path((*iter)->SubPartition_Of); - if (ParentPartition) - ParentPartition->Has_SubPartition = true; - else - LOGERR("Unable to locate parent partition '%s' of '%s'\n", (*iter)->SubPartition_Of.c_str(), (*iter)->Mount_Point.c_str()); - } - } - fclose(fp); - return true; } void TWPartitionManager::Setup_Settings_Storage_Partition(TWPartition* Part) { @@ -479,8 +495,6 @@ void TWPartitionManager::Output_Partition(TWPartition* Part) { printf("Is_Settings_Storage "); if (Part->Ignore_Blkid) printf("Ignore_Blkid "); - if (Part->Retain_Layout_Version) - printf("Retain_Layout_Version "); if (Part->Mount_To_Decrypt) printf("Mount_To_Decrypt "); if (Part->Can_Flash_Img) @@ -877,10 +891,12 @@ int TWPartitionManager::Run_Backup(bool adbbackup) { LOGINFO("Calculating backup details...\n"); DataManager::GetValue("tw_backup_list", Backup_List); + LOGINFO("Backup_List: %s\n", Backup_List.c_str()); if (!Backup_List.empty()) { end_pos = Backup_List.find(";", start_pos); while (end_pos != string::npos && start_pos < Backup_List.size()) { backup_path = Backup_List.substr(start_pos, end_pos - start_pos); + LOGINFO("backup_path: %s\n", backup_path.c_str()); part_settings.Part = Find_Partition_By_Path(backup_path); if (part_settings.Part != NULL) { partition_count++; @@ -1503,9 +1519,6 @@ int TWPartitionManager::Format_Data(void) { TWPartition* dat = Find_Partition_By_Path("/data"); if (dat != NULL) { - if (!dat->UnMount(true)) - return false; - return dat->Wipe_Encryption(); } else { gui_msg(Msg(msg::kError, "unable_to_locate=Unable to locate {1}.")("/data")); @@ -1665,11 +1678,13 @@ void TWPartitionManager::Update_System_Details(void) { TWPartition* FreeStorage = Find_Partition_By_Path(current_storage_path); if (FreeStorage != NULL) { // Attempt to mount storage - if (!FreeStorage->Mount(false)) { - gui_msg(Msg(msg::kError, "unable_to_mount_storage=Unable to mount storage")); - DataManager::SetValue(TW_STORAGE_FREE_SIZE, 0); - } else { - DataManager::SetValue(TW_STORAGE_FREE_SIZE, (int)(FreeStorage->Free / 1048576LLU)); + if (!TWFunc::Is_Mount_Wiped("/data")) { + if (!FreeStorage->Mount(false)) { + gui_msg(Msg(msg::kWarning, "unable_to_mount_storage=Unable to mount storage")); + DataManager::SetValue(TW_STORAGE_FREE_SIZE, 0); + } else { + DataManager::SetValue(TW_STORAGE_FREE_SIZE, (int)(FreeStorage->Free / 1048576LLU)); + } } } else { LOGINFO("Unable to find storage partition '%s'.\n", current_storage_path.c_str()); @@ -1681,6 +1696,9 @@ void TWPartitionManager::Update_System_Details(void) { void TWPartitionManager::Post_Decrypt(const string& Block_Device) { TWPartition* dat = Find_Partition_By_Path("/data"); +#ifdef USE_FSCRYPT + dat->Set_Block_Device("/dev/block/mapper/userdata"); +#endif if (dat != NULL) { DataManager::SetValue(TW_IS_DECRYPTED, 1); dat->Is_Decrypted = true; @@ -3326,8 +3344,14 @@ std::string TWPartitionManager::Get_Super_Partition() { void TWPartitionManager::Setup_Super_Devices() { std::string superPart = Get_Super_Partition(); android::fs_mgr::CreateLogicalPartitions(superPart); +} + +void TWPartitionManager::Setup_Super_Partition() { TWPartition* superPartition = new TWPartition(); - superPartition->Mount_Point = "super"; + std::string superPart = Get_Super_Partition(); + + superPartition->Backup_Path = "/super"; + superPartition->Mount_Point = "/super"; superPartition->Actual_Block_Device = superPart; superPartition->Alternate_Block_Device = superPart; superPartition->Backup_Display_Name = "super"; @@ -3336,6 +3360,7 @@ void TWPartitionManager::Setup_Super_Devices() { superPartition->Can_Be_Backed_Up = true; superPartition->Is_Present = true; superPartition->Is_SubPartition = false; + superPartition->Setup_Image(); Add_Partition(superPartition); PartitionManager.Output_Partition(superPartition); Update_System_Details(); diff --git a/partitions.hpp b/partitions.hpp index cfe69708..79197ed0 100755 --- a/partitions.hpp +++ b/partitions.hpp @@ -26,6 +26,7 @@ #include "exclude.hpp" #include "tw_atomic.hpp" #include "progresstracking.hpp" +#include "fscrypt_policy.h" #define MAX_FSTAB_LINE_LENGTH 2048 @@ -185,10 +186,10 @@ protected: private: bool Process_Fstab_Line(const char *fstab_line, bool Display_Error, std::map *twrp_flags, bool Sar_Detect); // Processes a fstab line void Setup_Data_Partition(bool Display_Error); // Setup data partition after fstab processed + void Set_FBE_Status(); // Set FBE status of partition void Setup_Cache_Partition(bool Display_Error); // Setup cache partition after fstab processed bool Find_Wildcard_Block_Devices(const string& Device); // Searches for and finds wildcard block devices void Find_Actual_Block_Device(); // Determines the correct block device and stores it in Actual_Block_Device - void Apply_TW_Flag(const unsigned flag, const char* str, const bool val); // Apply custom twrp fstab flags void Process_TW_Flags(char *flags, bool Display_Error, int fstab_ver); // Process custom twrp fstab flags void Process_FS_Flags(const char *str); // Process standard fstab fs flags @@ -212,6 +213,7 @@ private: bool Wipe_NTFS(); // Uses mkntfs to wipe bool Wipe_Data_Without_Wiping_Media(); // Uses rm -rf to wipe but does not wipe /data/media bool Wipe_Data_Without_Wiping_Media_Func(const string& parent); // Uses rm -rf to wipe but does not wipe /data/media + bool Recreate_AB_Cache_Dir(const fscrypt_encryption_policy &policy); // Recreate AB_CACHE_DIR after wipe void Wipe_Crypto_Key(); // Wipe crypto key from either footer or block device bool Backup_Tar(PartitionSettings *part_settings, pid_t *tar_fork_pid); // Backs up using tar for file systems bool Backup_Image(PartitionSettings *part_settings); // Backs up using raw read/write for emmc memory types @@ -279,7 +281,6 @@ private: string Mount_Options; // File system options from recovery.fstab unsigned long Format_Block_Size; // Block size for formatting bool Ignore_Blkid; // Ignore blkid results due to superblocks lying to us on certain devices / partitions - bool Retain_Layout_Version; // Retains the .layout_version file during a wipe (needed on devices like Sony Xperia T where /data and /data/media are separate partitions) bool Can_Flash_Img; // Indicates if this partition can have images flashed to it via the GUI bool Mount_Read_Only; // Only mount this partition as read-only bool Is_Adopted_Storage; // Indicates that this partition is for adopted storage (android_expand) @@ -312,6 +313,7 @@ public: public: int Process_Fstab(string Fstab_Filename, bool Display_Error, bool Sar_Detect); // Parses the fstab and populates the partitions int Write_Fstab(); // Creates /etc/fstab file that's used by the command line for mount commands + void Decrypt_Data(); // Decrypt Data if enabled void Output_Partition_Logging(); // Outputs partition information to the log void Output_Partition(TWPartition* Part); // Outputs partition details to the log int Mount_By_Path(string Path, bool Display_Error); // Mounts partition based on path (e.g. /system) @@ -390,6 +392,7 @@ public: std::string Get_Super_Partition(); // Get Super Partition block device path void Setup_Super_Devices(); // Setup logical dm devices on super partition bool Get_Super_Status(); // Return whether device has a super partition + void Setup_Super_Partition(); // Setup the super partition for backup and restore private: void Setup_Settings_Storage_Partition(TWPartition* Part); // Sets up settings storage diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk index a092f54a..c1f1fbe3 100755 --- a/prebuilt/Android.mk +++ b/prebuilt/Android.mk @@ -110,11 +110,25 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libminijail.so RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libunwindstack.so RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libasyncio.so + RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libcgrouprc.so + RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libbinderthreadstate.so + RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libsquashfs_utils.so + RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libjsoncpp.so + RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libmdnssd.so + RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libfec.so RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libinit.so RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libdl_android.so RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libprotobuf-cpp-lite.so RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libbinder.so - RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/bin/toybox + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libchrome.so + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libevent.so + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/bin/keystore + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/bin/keystore_cli_v2 + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/bin/hwservicemanager + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/bin/servicemanager + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/bin/vold_prepare_subdirs + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../vendor/bin/vndservicemanager + RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/bin/toybox else RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libc.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libdl.so @@ -255,7 +269,15 @@ ifeq ($(TW_INCLUDE_DUMLOCK), true) RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/htcdumlock endif ifeq ($(TW_INCLUDE_CRYPTO), true) - RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcryptfsfde.so + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libcryptfsfde.so + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libdexfile_support.so + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libf2fs_sparseblock.so + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../vendor/lib64/libnos_transport.so + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../vendor/lib64/libnos_datagram.so + else + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcryptfsfde.so + endif RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libhardware.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgpt_twrp.so @@ -268,7 +290,11 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) endif # FBE files ifeq ($(TW_INCLUDE_CRYPTO_FBE), true) - RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libe4crypt.so + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) + RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libtwrpfscrypt.so + else + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libe4crypt.so + endif RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgatekeeper.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster_messages.so RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeystore_binder.so @@ -514,16 +540,21 @@ include $(BUILD_PHONY_PACKAGE) #relink init include $(CLEAR_VARS) -LOCAL_MODULE := relink_init +LOCAL_MODULE := twrp_ramdisk LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT) RELINK_INIT := $(TARGET_RECOVERY_ROOT_OUT)/system/bin/init LOCAL_POST_INSTALL_CMD += $(RELINK) $(TARGET_RECOVERY_ROOT_OUT)/ $(RELINK_INIT) && \ - mv $(TARGET_RECOVERY_ROOT_OUT)/system/bin/ueventd $(TARGET_RECOVERY_ROOT_OUT)/sbin/ && \ + cp $(TARGET_RECOVERY_ROOT_OUT)/system/bin/ueventd $(TARGET_RECOVERY_ROOT_OUT)/sbin/ && \ ln -sf /init $(TARGET_RECOVERY_ROOT_OUT)/sbin/init && \ - ln -sf /init $(TARGET_RECOVERY_ROOT_OUT)/system/bin/init -LOCAL_REQUIRED_MODULES := init_second_stage.recovery reboot.recovery + ln -sf /init $(TARGET_RECOVERY_ROOT_OUT)/system/bin/init && \ + mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/system/etc/selinux/ && \ + cp $(TARGET_ROOT_OUT)/../system/etc/selinux/plat_service_contexts $(TARGET_RECOVERY_ROOT_OUT)/system/etc/selinux/plat_service_contexts && \ + cp $(TARGET_ROOT_OUT)/../system/etc/selinux/plat_hwservice_contexts $(TARGET_RECOVERY_ROOT_OUT)/system/etc/selinux/plat_hwservice_contexts && \ + cp $(TARGET_ROOT_OUT)/../vendor/etc/selinux/vndservice_contexts $(TARGET_RECOVERY_ROOT_OUT)/system/etc/selinux/vndservice_contexts && \ + cp $(TARGET_ROOT_OUT)/../vendor/etc/selinux/vendor_hwservice_contexts $(TARGET_RECOVERY_ROOT_OUT)/system/etc/selinux/vendor_hwservice_contexts +LOCAL_REQUIRED_MODULES := init_second_stage.recovery reboot.recovery plat_service_contexts plat_hardware_contexts vndservice_contexts include $(BUILD_PHONY_PACKAGE) #mke2fs.conf diff --git a/prebuilt/relink.sh b/prebuilt/relink.sh index dbf44e5f..783e0522 100755 --- a/prebuilt/relink.sh +++ b/prebuilt/relink.sh @@ -18,6 +18,7 @@ process_file() sed "s|/system/bin/linker64\x0|/sbin/linker64\x0\x0\x0\x0\x0\x0\x0|g" $src | sed "s|/system/bin/linker\x0|/sbin/linker\x0\x0\x0\x0\x0\x0\x0|g" | sed "s|/system/bin/sh\x0|/sbin/sh\x0\x0\x0\x0\x0\x0\x0|g" > $dst #rm -f $src + [ -e $2.tmp ] && rm $2.tmp || echo } diff --git a/twrp-functions.cpp b/twrp-functions.cpp index fa4e35d6..b20f6ff9 100755 --- a/twrp-functions.cpp +++ b/twrp-functions.cpp @@ -649,7 +649,8 @@ void TWFunc::Update_Intent_File(string Intent) { int TWFunc::tw_reboot(RebootCommand command) { DataManager::Flush(); - Update_Log_File(); + if (!Is_Mount_Wiped("/data")) + Update_Log_File(); // Always force a sync before we reboot sync(); @@ -1356,4 +1357,50 @@ void TWFunc::List_Mounts() { } } +bool TWFunc::Get_Encryption_Policy(fscrypt_encryption_policy &policy, std::string path) { + if (!TWFunc::Path_Exists(path)) { + LOGERR("Unable to find %s to get policy\n", path.c_str()); + return false; + } + if (!fscrypt_policy_get_struct(path.c_str(), &policy)) { + LOGERR("No policy set for path %s\n", path.c_str()); + return false; + } + return true; +} + +bool TWFunc::Set_Encryption_Policy(std::string path, const fscrypt_encryption_policy &policy) { + if (!TWFunc::Path_Exists(path)) { + LOGERR("unable to find %s to set policy\n", path.c_str()); + return false; + } + uint8_t binary_policy[FS_KEY_DESCRIPTOR_SIZE]; + char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(binary_policy, policy_hex); + if (!fscrypt_policy_set_struct(path.c_str(), &policy)) { + LOGERR("unable to set policy for path: %s\n", path.c_str()); + return false; + } + return true; +} + +bool TWFunc::Is_Mount_Wiped(std::string path) { + DIR* d = opendir(path.c_str()); + size_t file_count = 0; + if (d != NULL) { + struct dirent* de; + while ((de = readdir(d)) != NULL) { + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + continue; + if (strncmp(de->d_name, "lost+found", 10) == 0 || strncmp(de->d_name, "media", 5) == 0 || strncmp(de->d_name, "misc", 4) == 0 + || strncmp(de->d_name, "system", 6) == 0 || strncmp(de->d_name, "unencrypted", 11) == 0 + || strncmp(de->d_name, "per_boot", 8) == 0) + continue; + file_count++; + + } + closedir(d); + } + return file_count == 0; +} #endif // ndef BUILD_TWRPTAR_MAIN diff --git a/twrp-functions.hpp b/twrp-functions.hpp index 412920a6..5fad92ae 100755 --- a/twrp-functions.hpp +++ b/twrp-functions.hpp @@ -114,6 +114,9 @@ public: static void check_selinux_support(); // print whether selinux support is enabled to console static bool Is_TWRP_App_In_System(); // Check if the TWRP app is installed in the system partition static int Property_Override(string Prop_Name, string Prop_Value); // Override properties (including ro. properties) + static bool Get_Encryption_Policy(fscrypt_encryption_policy &policy, std::string path); // return encryption policy for path + static bool Set_Encryption_Policy(std::string path, const fscrypt_encryption_policy &policy); // set encryption policy for path + static bool Is_Mount_Wiped(std::string path); // check if directory has been wiped static void List_Mounts(); private: diff --git a/twrp.cpp b/twrp.cpp index 1b97f0b2..b5f922b5 100755 --- a/twrp.cpp +++ b/twrp.cpp @@ -70,6 +70,31 @@ static void Print_Prop(const char *key, const char *name, void *cookie) { printf("%s=%s\n", key, name); } +static void Decrypt_Page(bool SkipDecryption, bool datamedia) { + // Offer to decrypt if the device is encrypted + if (DataManager::GetIntValue(TW_IS_ENCRYPTED) != 0) { + if (SkipDecryption) { + LOGINFO("Skipping decryption\n"); + } else { + LOGINFO("Is encrypted, do decrypt page first\n"); + if (gui_startPage("decrypt", 1, 1) != 0) { + LOGERR("Failed to start decrypt GUI page.\n"); + } else { + // Check for and load custom theme if present + TWFunc::check_selinux_support(); + gui_loadCustomResources(); + } + } + } else if (datamedia) { + TWFunc::check_selinux_support(); + if (tw_get_default_metadata(DataManager::GetSettingsStoragePath().c_str()) != 0) { + LOGINFO("Failed to get default contexts and file mode for storage files.\n"); + } else { + LOGINFO("Got default contexts and file mode for storage files.\n"); + } + } +} + int main(int argc, char **argv) { // Recovery needs to install world-readable files, so clear umask // set by init @@ -199,9 +224,19 @@ int main(int argc, char **argv) { return -1; } PartitionManager.Output_Partition_Logging(); +#ifdef TW_INCLUDE_CRYPTO + DataManager::SetValue(TW_IS_ENCRYPTED, 1); +#endif - if (PartitionManager.Get_Super_Status()) + if (PartitionManager.Get_Super_Status()) { PartitionManager.Setup_Super_Devices(); + PartitionManager.Setup_Super_Partition(); + } else { +#ifdef TW_INCLUDE_CRYPTO + if (!PartitionManager.Get_Super_Status()) + PartitionManager.Decrypt_Data(); +#endif + } // Load up all the resources gui_loadResources(); @@ -310,28 +345,8 @@ int main(int argc, char **argv) { LOGINFO("Backup of TWRP ramdisk done.\n"); #endif - // Offer to decrypt if the device is encrypted - if (DataManager::GetIntValue(TW_IS_ENCRYPTED) != 0) { - if (SkipDecryption) { - LOGINFO("Skipping decryption\n"); - } else { - LOGINFO("Is encrypted, do decrypt page first\n"); - if (gui_startPage("decrypt", 1, 1) != 0) { - LOGERR("Failed to start decrypt GUI page.\n"); - } else { - // Check for and load custom theme if present - TWFunc::check_selinux_support(); - gui_loadCustomResources(); - } - } - } else if (datamedia) { - TWFunc::check_selinux_support(); - if (tw_get_default_metadata(DataManager::GetSettingsStoragePath().c_str()) != 0) { - LOGINFO("Failed to get default contexts and file mode for storage files.\n"); - } else { - LOGINFO("Got default contexts and file mode for storage files.\n"); - } - } + if (!PartitionManager.Get_Super_Status()) + Decrypt_Page(SkipDecryption, datamedia); // Fixup the RTC clock on devices which require it if (crash_counter == 0) @@ -339,7 +354,9 @@ int main(int argc, char **argv) { // Read the settings file TWFunc::Update_Log_File(); - DataManager::ReadSettingsFile(); + + if (!PartitionManager.Get_Super_Status()) + DataManager::ReadSettingsFile(); PageManager::LoadLanguage(DataManager::GetStrValue("tw_language")); GUIConsole::Translate_Now(); @@ -388,6 +405,11 @@ int main(int argc, char **argv) { LOGERR("Unable to load apex images from %s\n", APEX_DIR); } property_set("twrp.apex.loaded", "true"); +#ifdef TW_INCLUDE_CRYPTO + PartitionManager.Decrypt_Data(); + Decrypt_Page(SkipDecryption, datamedia); + DataManager::ReadSettingsFile(); +#endif } else { if ((DataManager::GetIntValue("tw_mount_system_ro") == 0 && sys->Check_Lifetime_Writes() == 0) || DataManager::GetIntValue("tw_mount_system_ro") == 2) { if (DataManager::GetIntValue("tw_never_show_system_ro_page") == 0) { @@ -410,6 +432,7 @@ int main(int argc, char **argv) { } } #endif + twrpAdbBuFifo *adb_bu_fifo = new twrpAdbBuFifo(); adb_bu_fifo->threadAdbBuFifo(); @@ -424,7 +447,8 @@ int main(int argc, char **argv) { // Reboot TWFunc::Update_Intent_File(Send_Intent); delete adb_bu_fifo; - TWFunc::Update_Log_File(); + if (!TWFunc::Is_Mount_Wiped("/data")) + TWFunc::Update_Log_File(); gui_msg(Msg("rebooting=Rebooting...")); string Reboot_Arg; DataManager::GetValue("tw_reboot_arg", Reboot_Arg); diff --git a/twrpTar.cpp b/twrpTar.cpp old mode 100644 new mode 100755 index b2d0ea67..c4a71560 --- a/twrpTar.cpp +++ b/twrpTar.cpp @@ -1,6 +1,6 @@ /* - Copyright 2013 to 2016 bigbiff/Dees_Troy TeamWin + Copyright 2013 to 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify @@ -47,6 +47,7 @@ extern "C" { #include "twrp-functions.hpp" #include "gui/gui.hpp" #include "progresstracking.hpp" + #ifndef BUILD_TWRPTAR_MAIN #include "data.hpp" #include "infomanager.hpp" @@ -54,8 +55,19 @@ extern "C" { #endif //ndef BUILD_TWRPTAR_MAIN #ifdef TW_INCLUDE_FBE +#ifdef USE_FSCRYPT +#include "fscrypt_policy.h" +#else #include "crypto/ext4crypt/ext4crypt_tar.h" -#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR |TAR_STORE_EXT4_POL +#endif +#endif + +#ifdef TW_INCLUDE_FBE +#ifdef USE_FSCRYPT +#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR | TAR_STORE_FSCRYPT_POL +#else +#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR | TAR_STORE_EXT4_POL +#endif #else #define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR #endif @@ -79,8 +91,12 @@ twrpTar::twrpTar(void) { output_fd = -1; backup_exclusions = NULL; #ifdef TW_INCLUDE_FBE +#ifdef USE_FSCRYPT + fscrypt_set_mode(); +#else e4crypt_set_mode(); #endif +#endif } twrpTar::~twrpTar(void) {