From 7d1222a5fe2c2dfa393e0d37cb58605734b9af7f Mon Sep 17 00:00:00 2001 From: nkk71 Date: Mon, 6 Nov 2017 17:27:21 +0200 Subject: vold_decrypt: FDE Keymaster 3.0 support * HTC U11 Oreo is using keymaster3 FDE encryption which requires the new services: 1- /system/bin/hwservicemanager 2- /vendor/bin/hw/android.hardware.keymaster@3.0-service 3- /vendor/bin/qseecomd (instead of /system/bin/qseecomd) So in addition to /vendor/lib and /vendor/lib64 also symlink /system/vendor/bin to /vendor/bin. * vold_decrypt services now have separate prefixes: 1- 'sys_' referring to /system/bin 2- 'ven_' referring to /vendor/bin * The additional (hwservicemanager, keymaster-3-0) and modified (qseecomd) .rc files have been updated in the vold_decrypt directory. Comments were added directly in the .rc files, please check them. * /etc/recovery.fstab needs to be temporarily moved since vold will use it if it finds the '/sbin/recovery' file (refer to fs_mgr for the fstab load code https://goo.gl/8KaZyf). Since fs_mgr cannot parse TWRP style fstab, we 'hide' it and attempt to create a symlink to /fstab.{ro.hardware}. Also remove shell dependencies, code cleanup, new error codes: * Critical sections of vold_decrypt should not rely on the external shell (and the available binaries) provided by TWFunc::Exec_Cmd. Doing so may lead to failures resulting from different shell provided binaries not working properly, especially since busybox can be inconsistent across different trees. In particular the following functions have been changed: * run_vdc() no longer uses daisy chained commands, instead it now forks and executes vdc directly including a 30 second built in timeout. * Symlink_Firmware_Files() no longer relies on the shell 'find' command to retrieve the list of firmware/vendor files and instead uses a built in function, Find_Firmware_Files(), which traverses the system partition to retrieve the list of files. * The code has also been cleaned up a little for better consistency, and vold_decrypt will now return various error codes for the different failures, as defined in vold_decrypt.h, which allows the gui_msg to be moved back to partitionmanager.cpp. Notes regarding pre Android 8.0 builds: * Service names in .rc files cannot exceed 16 characters (including the prepended 'sys_' or 'ven_') in Android 7.1 and below, so a service name such as 'sys_hwservicemanager' is out of the question for 7.1 and below. * hwservicemanager will check ACLs on 'hwservicemanager' and 'ITokenManager' if they are even allowed to run, otherwise the interfaces will fail. The policies have only been introduced in 8.0, and although it is possible to manually add them to the 7.1 policies it's not recommended. * Therefore the best course of action is to build in 8.0. * SIDE NOTE: On the HTC U11 we are actually using omni-7.1 with some changes in the device tree to support both Nougat and Oreo decryption, please refer to: 1- https://gerrit.twrp.me/c/2756/ for the necessary sepolicy and BoardConfig changes. 2- The Android.mk file for vold_decrypt was modified to truncate greater than 16 character service names (as mentioned therein) Other changes: * TW_CRYPTO_SYSTEM_VOLD_DISABLE_TIMEOUT is now deprecated due to built- in fork and timeout. * Output_dmesg_to_recovery_log() is also deprecated so upon a failed decryption the recovery.log will no longer append it, instead you can just use 'adb shell dmesg' to check it. Nonetheless if a true debug build is needed use the original TW_CRYPTO_SYSTEM_VOLD_DEBUG flag as outlined in the original commit message (see below). Usage info: This is an update to the initial vold_decrypt, for more info refer to https://github.com/omnirom/android_bootable_recovery/commit/71c6c50d0da1f32dd18a749797e88de2358c5ba1 Change-Id: Id7129d125ae7f5dcba0779489825add718022ba3 --- crypto/vold_decrypt/Android.mk | 26 +- .../init.recovery.vold_decrypt.hwservicemanager.rc | 18 + .../init.recovery.vold_decrypt.keymaster-3-0.rc | 17 + .../init.recovery.vold_decrypt.qseecomd.rc | 12 + crypto/vold_decrypt/vold_decrypt.cpp | 1108 +++++++++++++++----- crypto/vold_decrypt/vold_decrypt.h | 18 +- partitionmanager.cpp | 10 + 7 files changed, 911 insertions(+), 298 deletions(-) create mode 100755 crypto/vold_decrypt/init.recovery.vold_decrypt.hwservicemanager.rc create mode 100755 crypto/vold_decrypt/init.recovery.vold_decrypt.keymaster-3-0.rc diff --git a/crypto/vold_decrypt/Android.mk b/crypto/vold_decrypt/Android.mk index e371c24e7..ddfad0327 100644 --- a/crypto/vold_decrypt/Android.mk +++ b/crypto/vold_decrypt/Android.mk @@ -16,7 +16,6 @@ LOCAL_PATH := $(call my-dir) ifeq ($(TW_INCLUDE_CRYPTO), true) ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),) - ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false) # Parse TW_CRYPTO_USE_SYSTEM_VOLD @@ -54,6 +53,17 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) cp -f "$(LOCAL_PATH)/$(item)" "$(TARGET_ROOT_OUT)"/; \ fi; \ ) + + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0) + # Truncate service_name to max 16 characters + LOCAL_POST_INSTALL_CMD += \ + $(foreach item, $(rc_files), \ + if [ -f "$(TARGET_ROOT_OUT)/$(item)" ]; then \ + sed -i 's/\([ \t]*service[ \t]*\)\(.\{16\}\).*\([ \t].*\)/\1\2\3/' "$(TARGET_ROOT_OUT)/$(item)"; \ + fi; \ + ) + endif + include $(BUILD_PREBUILT) @@ -66,7 +76,14 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) endif ifneq ($(services),) - LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_SERVICES='"$(services)"' + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0) + # Truncate service_name to max 12 characters due to the 4 character prefix + truncated_services := $(foreach item,$(services),$(shell echo -n "$(item)" | sed 's/\(.\{12\}\).*/\1/')) + LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_SERVICES='"$(truncated_services)"' + LOCAL_CFLAGS += -D_USING_SHORT_SERVICE_NAMES + else + LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_SERVICES='"$(services)"' + endif endif ifeq ($(TW_CRYPTO_SYSTEM_VOLD_DEBUG),true) @@ -74,14 +91,9 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_DEBUG endif - ifeq ($(TW_CRYPTO_SYSTEM_VOLD_DISABLE_TIMEOUT),true) - LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_DISABLE_TIMEOUT - endif - LOCAL_SRC_FILES = vold_decrypt.cpp LOCAL_SHARED_LIBRARIES := libcutils include $(BUILD_STATIC_LIBRARY) endif - endif endif diff --git a/crypto/vold_decrypt/init.recovery.vold_decrypt.hwservicemanager.rc b/crypto/vold_decrypt/init.recovery.vold_decrypt.hwservicemanager.rc new file mode 100755 index 000000000..ab8b4ac4d --- /dev/null +++ b/crypto/vold_decrypt/init.recovery.vold_decrypt.hwservicemanager.rc @@ -0,0 +1,18 @@ +# Service names must be less than 16 characters in android-7.1 and +# below. The makefile will truncate service names if needed in any +# init.recovery.vold_decryp.*.rc file found in the vold_decrypt +# directory. +# It cannot however do this for any .rc file(s) that may be +# overridden by the device tree files! + +# The seclabels are not needed when built in Android 8.0 tree +# in 7.1 however the below do not exist, so run them under vold +service sys_hwservicemanager /system/bin/hwservicemanager + user root + group root + setenv PATH /system/bin + setenv LD_LIBRARY_PATH /system/lib64:/system/lib + onrestart setprop hwservicemanager.ready false + disabled + oneshot + seclabel u:r:vold:s0 diff --git a/crypto/vold_decrypt/init.recovery.vold_decrypt.keymaster-3-0.rc b/crypto/vold_decrypt/init.recovery.vold_decrypt.keymaster-3-0.rc new file mode 100755 index 000000000..e9f0b0241 --- /dev/null +++ b/crypto/vold_decrypt/init.recovery.vold_decrypt.keymaster-3-0.rc @@ -0,0 +1,17 @@ +# Service names must be less than 16 characters in android-7.1 and +# below. The makefile will truncate service names if needed in any +# init.recovery.vold_decryp.*.rc file found in the vold_decrypt +# directory. +# It cannot however do this for any .rc file(s) that may be +# overridden by the device tree files! + +# The seclabels are not needed when built in Android 8.0 tree +# in 7.1 however the below do not exist, so run them under vold +service ven_keymaster-3-0 /vendor/bin/hw/android.hardware.keymaster@3.0-service + user root + group root + setenv PATH /vendor/bin:/system/bin + setenv LD_LIBRARY_PATH /vendor/lib64:/system/lib64:/vendor/lib:/system/lib + disabled + oneshot + seclabel u:r:vold:s0 diff --git a/crypto/vold_decrypt/init.recovery.vold_decrypt.qseecomd.rc b/crypto/vold_decrypt/init.recovery.vold_decrypt.qseecomd.rc index 06bdebcd3..08666085d 100755 --- a/crypto/vold_decrypt/init.recovery.vold_decrypt.qseecomd.rc +++ b/crypto/vold_decrypt/init.recovery.vold_decrypt.qseecomd.rc @@ -5,6 +5,10 @@ on fs chmod 0664 /dev/ion chown system system /dev/ion + +# Oreo has qseecomd in /vendor/bin so add the additional +# service. Only an existing binary will be started, never both. + service sys_qseecomd /system/bin/qseecomd user root group root @@ -12,3 +16,11 @@ service sys_qseecomd /system/bin/qseecomd setenv LD_LIBRARY_PATH /system/lib64:/system/lib disabled oneshot + +service ven_qseecomd /vendor/bin/qseecomd + user root + group root + setenv PATH /vendor/bin:/system/bin + setenv LD_LIBRARY_PATH /vendor/lib64:/system/lib64:/vendor/lib:/system/lib + disabled + oneshot diff --git a/crypto/vold_decrypt/vold_decrypt.cpp b/crypto/vold_decrypt/vold_decrypt.cpp index d535a2514..f6e6e3482 100644 --- a/crypto/vold_decrypt/vold_decrypt.cpp +++ b/crypto/vold_decrypt/vold_decrypt.cpp @@ -16,28 +16,32 @@ along with TWRP. If not, see . */ - #include #include #include #include #include +#include +#include #include +#include +#include -#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG #include #include #include -#endif +#include #include #include #include -#include "../../twcommon.h" +#ifdef _USING_SHORT_SERVICE_NAMES +#include +#endif + #include "../../partitions.hpp" #include "../../twrp-functions.hpp" -#include "../../gui/gui.hpp" using namespace std; @@ -45,6 +49,10 @@ extern "C" { #include } +#include "vold_decrypt.h" + +namespace { + /* Timeouts as defined by ServiceManager */ /* The maximum amount of time to wait for a service to start or stop, @@ -55,51 +63,90 @@ extern "C" { #define SLEEP_MIN_USEC 200000 /* 200 msec */ -#define LOGDECRYPT(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]" __VA_ARGS__); fflush(fp_kmsg); } } while (0) -#define LOGDECRYPT_KMSG(...) do { if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]" __VA_ARGS__); fflush(fp_kmsg); } } while (0) +/* vold response codes defined in ResponseCode.h */ +// 200 series - Requested action has been successfully completed +#define COMMAND_OKAY 200 +#define PASSWORD_TYPE_RESULT 213 -#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES -typedef struct { - string service_name; - string twrp_svc_name; - bool is_running; - bool resume; -} AdditionalService; -#endif + +#define LOGINFO(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]I:" __VA_ARGS__); fflush(fp_kmsg); } } while (0) +#define LOGKMSG(...) do { if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]K:" __VA_ARGS__); fflush(fp_kmsg); } } while (0) +#define LOGERROR(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]E:" __VA_ARGS__); fflush(fp_kmsg); } } while (0) FILE *fp_kmsg = NULL; -bool has_timeout = false; + +/* Debugging Functions */ #ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG + +#ifndef VD_STRACE_BIN +#define VD_STRACE_BIN "/sbin/strace" +#endif + bool has_strace = false; +pid_t pid_strace = 0; -pid_t strace_init(void) { - if (!has_strace) - return -1; +void Strace_init_Start(void) { + has_strace = TWFunc::Path_Exists(VD_STRACE_BIN); + if (!has_strace) { + LOGINFO("strace binary (%s) not found, disabling strace in vold_decrypt!\n", VD_STRACE_BIN); + return; + } pid_t pid; switch(pid = fork()) { case -1: - LOGDECRYPT_KMSG("forking strace_init failed: %d!\n", errno); - return -1; + LOGKMSG("forking strace_init failed: %d (%s)!\n", errno, strerror(errno)); + return; case 0: // child - execl("/sbin/strace", "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", "/tmp/strace_init.log", "-p", "1" , NULL); - LOGDECRYPT_KMSG("strace_init fork failed: %d!\n", errno); + execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", "/tmp/strace_init.log", "-p", "1" , NULL); + LOGKMSG("strace_init fork failed: %d (%s)!\n", errno, strerror(errno)); exit(-1); default: - LOGDECRYPT_KMSG("Starting strace_init (pid=%d)\n", pid); - return pid; + LOGKMSG("Starting strace_init (pid=%d)\n", pid); + pid_strace = pid; + return; } } -#endif + +void Strace_init_Stop(void) { + if (pid_strace > 0) { + LOGKMSG("Stopping strace_init (pid=%d)\n", pid_strace); + int timeout; + int status; + pid_t retpid = waitpid(pid_strace, &status, WNOHANG); + + kill(pid_strace, SIGTERM); + for (timeout = 5; retpid == 0 && timeout; --timeout) { + sleep(1); + retpid = waitpid(pid_strace, &status, WNOHANG); + } + if (retpid) + LOGKMSG("strace_init terminated successfully\n"); + else { + // SIGTERM didn't work, kill it instead + kill(pid_strace, SIGKILL); + for (timeout = 5; retpid == 0 && timeout; --timeout) { + sleep(1); + retpid = waitpid(pid_strace, &status, WNOHANG); + } + if (retpid) + LOGKMSG("strace_init killed successfully\n"); + else + LOGKMSG("strace_init took too long to kill, may be a zombie process\n"); + } + } +} +#endif // TW_CRYPTO_SYSTEM_VOLD_DEBUG + /* Convert a binary key of specified length into an ascii hex string equivalent, * without the leading 0x and with null termination * * Original code from cryptfs.c */ -string convert_key_to_hex_ascii(string master_key) { +string convert_key_to_hex_ascii(const string& master_key) { size_t i; unsigned char nibble; string master_key_ascii = ""; @@ -117,7 +164,46 @@ string convert_key_to_hex_ascii(string master_key) { return master_key_ascii; } -string wait_for_property(string property_name, int utimeout = SLEEP_MAX_USEC, string expected_value = "not_empty") { +/* Helper Functions */ +#define PATH_EXISTS(path) (access(path, F_OK) >= 0) + +int vrename(const string& oldname, const string& newname, bool verbose = false) { + const char *old_name = oldname.c_str(); + const char *new_name = newname.c_str(); + + if (!PATH_EXISTS(old_name)) + return 0; + + if (rename(old_name, new_name) < 0) { + LOGERROR("Moving %s to %s failed: %d (%s)\n", old_name, new_name, errno, strerror(errno)); + return -1; + } else if (verbose) + LOGINFO("Renamed %s to %s\n", old_name, new_name); + else + LOGKMSG("Renamed %s to %s\n", old_name, new_name); + return 0; +} + +int vsymlink(const string& oldname, const string& newname, bool verbose = false) { + const char *old_name = oldname.c_str(); + const char *new_name = newname.c_str(); + + if (!PATH_EXISTS(old_name)) + return 0; + + if (symlink(old_name, new_name) < 0) { + LOGERROR("Symlink %s -> %s failed: %d (%s)\n", new_name, old_name, errno, strerror(errno)); + return -1; + } else if (verbose) + LOGINFO("Symlinked %s -> %s\n", new_name, old_name); + else + LOGKMSG("Symlinked %s -> %s\n", new_name, old_name); + return 0; +} + + +/* Properties and Services Functions */ +string Wait_For_Property(const string& property_name, int utimeout = SLEEP_MAX_USEC, const string& expected_value = "not_empty") { char prop_value[PROPERTY_VALUE_MAX]; if (expected_value == "not_empty") { @@ -125,7 +211,7 @@ string wait_for_property(string property_name, int utimeout = SLEEP_MAX_USEC, st property_get(property_name.c_str(), prop_value, "error"); if (strcmp(prop_value, "error") != 0) break; - LOGDECRYPT_KMSG("waiting for %s to get set\n", property_name.c_str()); + LOGKMSG("waiting for %s to get set\n", property_name.c_str()); utimeout -= SLEEP_MIN_USEC; usleep(SLEEP_MIN_USEC);; } @@ -135,7 +221,7 @@ string wait_for_property(string property_name, int utimeout = SLEEP_MAX_USEC, st property_get(property_name.c_str(), prop_value, "error"); if (strcmp(prop_value, expected_value.c_str()) == 0) break; - LOGDECRYPT_KMSG("waiting for %s to change from '%s' to '%s'\n", property_name.c_str(), prop_value, expected_value.c_str()); + LOGKMSG("waiting for %s to change from '%s' to '%s'\n", property_name.c_str(), prop_value, expected_value.c_str()); utimeout -= SLEEP_MIN_USEC; usleep(SLEEP_MIN_USEC);; } @@ -145,179 +231,84 @@ string wait_for_property(string property_name, int utimeout = SLEEP_MAX_USEC, st return prop_value; } -bool Service_Exists(string initrc_svc) { +string Get_Service_State(const string& initrc_svc) { char prop_value[PROPERTY_VALUE_MAX]; string init_svc = "init.svc." + initrc_svc; property_get(init_svc.c_str(), prop_value, "error"); - return (strcmp(prop_value, "error") != 0); + return prop_value; } -bool Is_Service_Running(string initrc_svc) { - char prop_value[PROPERTY_VALUE_MAX]; - string init_svc = "init.svc." + initrc_svc; - property_get(init_svc.c_str(), prop_value, "error"); - return (strcmp(prop_value, "running") == 0); +bool Service_Exists(const string& initrc_svc) { + return (Get_Service_State(initrc_svc) != "error"); } -bool Is_Service_Stopped(string initrc_svc) { - char prop_value[PROPERTY_VALUE_MAX]; - string init_svc = "init.svc." + initrc_svc; - property_get(init_svc.c_str(), prop_value, "error"); - return (strcmp(prop_value, "stopped") == 0); +bool Is_Service_Running(const string& initrc_svc) { + return (Get_Service_State(initrc_svc) == "running"); } -bool Start_Service(string initrc_svc, int utimeout = SLEEP_MAX_USEC) { +bool Is_Service_Stopped(const string& initrc_svc) { + return (Get_Service_State(initrc_svc) == "stopped"); +} + +bool Start_Service(const string& initrc_svc, int utimeout = SLEEP_MAX_USEC) { string res = "error"; string init_svc = "init.svc." + initrc_svc; property_set("ctl.start", initrc_svc.c_str()); - res = wait_for_property(init_svc, utimeout, "running"); + res = Wait_For_Property(init_svc, utimeout, "running"); - LOGDECRYPT("Start service %s: %s.\n", initrc_svc.c_str(), res.c_str()); + LOGINFO("Start service %s: %s.\n", initrc_svc.c_str(), res.c_str()); return (res == "running"); } -bool Stop_Service(string initrc_svc, int utimeout = SLEEP_MAX_USEC) { +bool Stop_Service(const string& initrc_svc, int utimeout = SLEEP_MAX_USEC) { string res = "error"; if (Service_Exists(initrc_svc)) { string init_svc = "init.svc." + initrc_svc; property_set("ctl.stop", initrc_svc.c_str()); - res = wait_for_property(init_svc, utimeout, "stopped"); - LOGDECRYPT("Stop service %s: %s.\n", initrc_svc.c_str(), res.c_str()); + res = Wait_For_Property(init_svc, utimeout, "stopped"); + LOGINFO("Stop service %s: %s.\n", initrc_svc.c_str(), res.c_str()); } return (res == "stopped"); } -void output_dmesg_to_recoverylog(void) { - TWFunc::Exec_Cmd( - "echo \"---- DMESG LOG FOLLOWS ----\";" - "dmesg | grep 'DECRYPT\\|vold\\|qseecom\\|QSEECOM\\|keymaste\\|keystore\\|cmnlib';" - "echo \"---- DMESG LOG ENDS ----\"" - ); -} -void set_needed_props(void) { - // vold won't start without ro.storage_structure on Kitkat - string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk"); - int sdkver = 20; - if (!sdkverstr.empty()) { - sdkver = atoi(sdkverstr.c_str()); - } - if (sdkver <= 19) { - string ro_storage_structure = TWFunc::System_Property_Get("ro.storage_structure"); - if (!ro_storage_structure.empty()) - property_set("ro.storage_structure", ro_storage_structure.c_str()); - } +/* Vendor, Firmware and fstab symlink Functions */ +bool is_Vendor_Mounted(void) { + static int is_mounted = -1; + if (is_mounted < 0) + is_mounted = PartitionManager.Is_Mounted_By_Path("/vendor") ? 1 : 0; + return is_mounted; } -string vdc_cryptfs_cmd(string log_name) { - string cmd = "LD_LIBRARY_PATH=/system/lib64:/system/lib /system/bin/vdc cryptfs"; - -#ifndef TW_CRYPTO_SYSTEM_VOLD_DEBUG - (void)log_name; // do nothing, but get rid of compiler warning in non debug builds -#else - if (has_timeout && has_strace) - cmd = "/sbin/strace -q -tt -ff -v -y -s 1000 -o /tmp/strace_vdc_" + log_name + " /sbin/timeout -t 30 -s KILL env " + cmd; - else if (has_strace) - cmd = "/sbin/strace -q -tt -ff -v -y -s 1000 -o /tmp/strace_vdc_" + log_name + " -E " + cmd; - else -#endif - if (has_timeout) - cmd = "/sbin/timeout -t 30 -s KILL env " + cmd; - - return cmd; +bool is_Firmware_Mounted(void) { + static int is_mounted = -1; + if (is_mounted < 0) + is_mounted = PartitionManager.Is_Mounted_By_Path("/firmware") ? 1 : 0; + return is_mounted; } -int run_vdc(string Password) { - int res = -1; - struct timeval t1, t2; - string vdc_res; - int vdc_r1, vdc_r2, vdc_r3; - - LOGDECRYPT("About to run vdc...\n"); - - // Wait for vold connection - gettimeofday(&t1, NULL); - t2 = t1; - while ((t2.tv_sec - t1.tv_sec) < 5) { - vdc_res.clear(); - // cryptfs getpwtype returns: R1=213(PasswordTypeResult) R2=? R3="password", "pattern", "pin", "default" - res = TWFunc::Exec_Cmd(vdc_cryptfs_cmd("connect") + " getpwtype", vdc_res); - std::replace(vdc_res.begin(), vdc_res.end(), '\n', ' '); // remove newline(s) - vdc_r1 = vdc_r2 = vdc_r3 = -1; - sscanf(vdc_res.c_str(), "%d", &vdc_r1); - if (vdc_r1 == 213) { - char str_res[sizeof(int) + 1]; - snprintf(str_res, sizeof(str_res), "%d", res); - vdc_res += "ret="; - vdc_res += str_res; - res = 0; - break; - } - LOGDECRYPT("Retrying connection to vold\n"); - usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts - gettimeofday(&t2, NULL); - } - - if (res != 0) - return res; - - LOGDECRYPT("Connected to vold (%s)\n", vdc_res.c_str()); - - // Input password from GUI, or default password - vdc_res.clear(); - res = TWFunc::Exec_Cmd(vdc_cryptfs_cmd("passwd") + " checkpw '" + Password + "'", vdc_res); - std::replace(vdc_res.begin(), vdc_res.end(), '\n', ' '); // remove newline(s) - LOGDECRYPT("vdc cryptfs result (passwd): %s (ret=%d)\n", vdc_res.c_str(), res); - vdc_r1 = vdc_r2 = vdc_r3 = -1; - sscanf(vdc_res.c_str(), "%d %d %d", &vdc_r1, &vdc_r2, &vdc_r3); - - if (vdc_r3 != 0) { - // try falling back to Lollipop hex passwords - string hexPassword = convert_key_to_hex_ascii(Password); - vdc_res.clear(); - res = TWFunc::Exec_Cmd(vdc_cryptfs_cmd("hex_pw") + " checkpw '" + hexPassword + "'", vdc_res); - std::replace(vdc_res.begin(), vdc_res.end(), '\n', ' '); // remove newline(s) - LOGDECRYPT("vdc cryptfs result (hex_pw): %s (ret=%d)\n", vdc_res.c_str(), res); - vdc_r1 = vdc_r2 = vdc_r3 = -1; - sscanf(vdc_res.c_str(), "%d %d %d", &vdc_r1, &vdc_r2, &vdc_r3); - } - - // vdc's return value is dependant upon source origin, it will either - // return 0 or vdc_r1, so disregard and focus on decryption instead - if (vdc_r3 == 0) { - // Decryption successful wait for crypto blk dev - wait_for_property("ro.crypto.fs_crypto_blkdev"); - res = 0; - } else { - res = -1; - } - - return res; +bool will_VendorBin_Be_Symlinked(void) { + return (!is_Vendor_Mounted() && TWFunc::Path_Exists("/system/vendor")); } bool Symlink_Vendor_Folder(void) { bool is_vendor_symlinked = false; - if (PartitionManager.Is_Mounted_By_Path("/vendor")) { - LOGDECRYPT("vendor partition mounted, skipping /vendor substitution\n"); + if (is_Vendor_Mounted()) { + LOGINFO("vendor partition mounted, skipping /vendor substitution\n"); } else if (TWFunc::Path_Exists("/system/vendor")) { - LOGDECRYPT("Symlinking vendor folder...\n"); - if (TWFunc::Path_Exists("/vendor") && rename("/vendor", "/vendor-orig") != 0) { - LOGDECRYPT("Failed to rename original /vendor folder: %s\n", strerror(errno)); - } else { + LOGINFO("Symlinking vendor folder...\n"); + if (!TWFunc::Path_Exists("/vendor") || vrename("/vendor", "/vendor-orig") == 0) { TWFunc::Recursive_Mkdir("/vendor/firmware/keymaster"); - LOGDECRYPT_KMSG("Symlinking /system/vendor/lib64 to /vendor/lib64 (res=%d)\n", - symlink("/system/vendor/lib64", "/vendor/lib64") - ); - LOGDECRYPT_KMSG("Symlinking /system/vendor/lib to /vendor/lib (res=%d)\n", - symlink("/system/vendor/lib", "/vendor/lib") - ); + vsymlink("/system/vendor/lib64", "/vendor/lib64"); + vsymlink("/system/vendor/lib", "/vendor/lib"); + vsymlink("/system/vendor/bin", "/vendor/bin"); is_vendor_symlinked = true; property_set("vold_decrypt.symlinked_vendor", "1"); } @@ -328,19 +319,18 @@ bool Symlink_Vendor_Folder(void) { void Restore_Vendor_Folder(void) { property_set("vold_decrypt.symlinked_vendor", "0"); TWFunc::removeDir("/vendor", false); - rename("/vendor-orig", "/vendor"); + vrename("/vendor-orig", "/vendor"); } bool Symlink_Firmware_Folder(void) { bool is_firmware_symlinked = false; - if (PartitionManager.Is_Mounted_By_Path("/firmware")) { - LOGDECRYPT("firmware partition mounted, skipping /firmware substitution\n"); - } else { - LOGDECRYPT("Symlinking firmware folder...\n"); - if (TWFunc::Path_Exists("/firmware") && rename("/firmware", "/firmware-orig") != 0) { - LOGDECRYPT("Failed to rename original /firmware folder: %s\n", strerror(errno)); - } else { + if (is_Firmware_Mounted()) { + LOGINFO("firmware partition mounted, skipping /firmware substitution\n"); + } + else { + LOGINFO("Symlinking firmware folder...\n"); + if (!TWFunc::Path_Exists("/firmware") || vrename("/firmware", "/firmware-orig") == 0) { TWFunc::Recursive_Mkdir("/firmware/image"); is_firmware_symlinked = true; property_set("vold_decrypt.symlinked_firmware", "1"); @@ -352,103 +342,674 @@ bool Symlink_Firmware_Folder(void) { void Restore_Firmware_Folder(void) { property_set("vold_decrypt.symlinked_firmware", "0"); TWFunc::removeDir("/firmware", false); - rename("/firmware-orig", "/firmware"); + vrename("/firmware-orig", "/firmware"); +} + +int Find_Firmware_Files(const string& Path, vector *FileList) { + int ret; + DIR* d; + struct dirent* de; + string FileName; + + d = opendir(Path.c_str()); + if (d == NULL) { + closedir(d); + return -1; + } + while ((de = readdir(d)) != NULL) { + if (de->d_type == DT_DIR) { + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + continue; + FileName = Path + "/" + de->d_name; + ret = Find_Firmware_Files(FileName, FileList); + if (ret < 0) + return -1; + } else if (de->d_type == DT_REG) { + if (fnmatch("keymaste*.*", de->d_name, 0) == 0 || fnmatch("cmnlib.*", de->d_name, 0) == 0) { + FileName = Path + "/" + de->d_name; + FileList->push_back(FileName); + } + } + } + closedir(d); + return 0; } void Symlink_Firmware_Files(bool is_vendor_symlinked, bool is_firmware_symlinked) { if (!is_vendor_symlinked && !is_firmware_symlinked) return; - LOGDECRYPT("Symlinking firmware files...\n"); - string result_of_find; - TWFunc::Exec_Cmd("find /system -name keymaste*.* -type f -o -name cmnlib.* -type f 2>/dev/null", result_of_find); + LOGINFO("Symlinking firmware files...\n"); - stringstream ss(result_of_find); - string line; - int count = 0; + vector FirmwareFiles; + Find_Firmware_Files("/system", &FirmwareFiles); - while(getline(ss, line)) { - const char *fwfile = line.c_str(); - string base_name = TWFunc::Get_Filename(line); - count++; + for (size_t i = 0; i < FirmwareFiles.size(); ++i) { + string base_name = TWFunc::Get_Filename(FirmwareFiles[i]); - if (is_firmware_symlinked) { - LOGDECRYPT_KMSG("Symlinking %s to /firmware/image/ (res=%d)\n", fwfile, - symlink(fwfile, ("/firmware/image/" + base_name).c_str()) - ); - } + if (is_firmware_symlinked) + vsymlink(FirmwareFiles[i], "/firmware/image/" + base_name); if (is_vendor_symlinked) { - LOGDECRYPT_KMSG("Symlinking %s to /vendor/firmware/ (res=%d)\n", fwfile, - symlink(fwfile, ("/vendor/firmware/" + base_name).c_str()) - ); + vsymlink(FirmwareFiles[i], "/vendor/firmware/" + base_name); + vsymlink(FirmwareFiles[i], "/vendor/firmware/keymaster/" + base_name); + } + } + LOGINFO("%d file(s) symlinked.\n", (int)FirmwareFiles.size()); +} - LOGDECRYPT_KMSG("Symlinking %s to /vendor/firmware/keymaster/ (res=%d)\n", fwfile, - symlink(fwfile, ("/vendor/firmware/keymaster/" + base_name).c_str()) - ); +// Android 8.0 fs_mgr checks for "/sbin/recovery", in which case it will +// use /etc/recovery.fstab -> symlink it temporarily. Reference: +// https://android.googlesource.com/platform/system/core/+/android-8.0.0_r17/fs_mgr/fs_mgr_fstab.cpp#693 +bool Symlink_Recovery_Fstab(void) { + bool is_fstab_symlinked = false; + + if (vrename("/etc/recovery.fstab", "/etc/recovery-fstab-orig") == 0) { + is_fstab_symlinked = true; + + // now attempt to symlink to /fstab.{ro.hardware}, but even if that + // fails, keep TWRP's fstab hidden since it cannot be parsed by fs_mgr + char prop_value[PROPERTY_VALUE_MAX]; + property_get("ro.hardware", prop_value, "error"); + if (strcmp(prop_value, "error")) { + string fstab_device = "/fstab."; fstab_device += prop_value; + vsymlink(fstab_device, "/etc/recovery.fstab"); } } - LOGDECRYPT("%d file(s) symlinked.\n", count); + return is_fstab_symlinked; } +void Restore_Recovery_Fstab(void) { + unlink("/etc/recovery.fstab"); + vrename("/etc/recovery-fstab-orig", "/etc/recovery.fstab"); +} + + +/* Additional Services Functions */ #ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES -vector Get_List_Of_Additional_Services (void) { +typedef struct { + string Service_Name; + string Service_Path; + string Service_Binary; + + string VOLD_Service_Name; + string TWRP_Service_Name; + bool is_running; + bool resume; + bool exists; +} AdditionalService; + +typedef struct { + string Service_Name; + string Service_Path; + string Service_Binary; +} RC_Service; + +// expand_props() courtesy of platform_system_core_init_util.cpp +bool expand_props(const std::string& src, std::string* dst) { + const char* src_ptr = src.c_str(); + + if (!dst) { + return false; + } + + /* - variables can either be $x.y or ${x.y}, in case they are only part + * of the string. + * - will accept $$ as a literal $. + * - no nested property expansion, i.e. ${foo.${bar}} is not supported, + * bad things will happen + * - ${x.y:-default} will return default value if property empty. + */ + while (*src_ptr) { + const char* c; + + c = strchr(src_ptr, '$'); + if (!c) { + dst->append(src_ptr); + return true; + } + + dst->append(src_ptr, c); + c++; + + if (*c == '$') { + dst->push_back(*(c++)); + src_ptr = c; + continue; + } else if (*c == '\0') { + return true; + } + + std::string prop_name; + std::string def_val; + if (*c == '{') { + c++; + const char* end = strchr(c, '}'); + if (!end) { + // failed to find closing brace, abort. + return false; + } + prop_name = std::string(c, end); + c = end + 1; + size_t def = prop_name.find(":-"); + if (def < prop_name.size()) { + def_val = prop_name.substr(def + 2); + prop_name = prop_name.substr(0, def); + } + } else { + prop_name = c; + c += prop_name.size(); + } + + if (prop_name.empty()) { + return false; + } + + char prop_value[PROPERTY_VALUE_MAX]; + property_get(prop_name.c_str(), prop_value, ""); + std::string prop_val = prop_value; + if (prop_val.empty()) { + if (def_val.empty()) { + return false; + } + prop_val = def_val; + } + + dst->append(prop_val); + src_ptr = c; + } + + return true; +} + +string GetArgument(const string& line, size_t argument_number, bool expand_properties) { + size_t beg; + size_t end; + string argument; + + beg = line.find_first_not_of(" \t\r"); + if (beg == string::npos) + return ""; + + for (size_t i = 0; i < argument_number; ++i) { + end = line.find_first_of(" \t\r", beg); + if (end == string::npos) + return ""; + + beg = line.find_first_not_of(" \t\r", end); + if (beg == string::npos) + return ""; + } + + end = line.find_first_of(" \t\r", beg); + if (end == string::npos) + argument = line.substr(beg); + else + argument = line.substr(beg, end - beg); // exclude trailing whitespace + + if (expand_properties) { + string expanded_property_argument; + if (expand_props(argument, &expanded_property_argument)) + return expanded_property_argument; + else + return ""; + } else { + return argument; + } +} + +// Very simplified .rc parser to get services +void Parse_RC_File(const string& rc_file, vector& RC_Services) { + ifstream file; + + file.open(rc_file.c_str(), ios::in); + if (!file.is_open()) + return; + + size_t beg; // left trim + size_t end; // right trim + bool continuation = false; // backslash continuation + string line; // line + string real_line; // trimmed line with backslash continuation removal + vector imports; // file names of imports (we don't want to recursively do while the file is open) + + while (getline(file, line)) { + beg = line.find_first_not_of(" \t\r"); + end = line.find_last_not_of(" \t\r"); + if (end == string::npos) + end = line.length(); + + if (beg == string::npos) { + if (continuation) + continuation = false; + else + continue; + } else if (line[end] == '\\') { + continuation = true; + real_line += line.substr(beg, end - beg); // excluding backslash + continue; + } else if (continuation) { + continuation = false; + real_line += line.substr(beg, end - beg + 1); + } else { + real_line = line.substr(beg, end - beg + 1); + } + + if (GetArgument(real_line, 0, false) == "import") { + // handle: import + string file_name = GetArgument(real_line, 1, true); + if (file_name.empty()) { + // INVALID IMPORT + } else + imports.push_back(file_name); + } else if (GetArgument(real_line, 0, false) == "service") { + // handle: service + RC_Service svc; + + svc.Service_Name = GetArgument(real_line, 1, false); + svc.Service_Path = GetArgument(real_line, 2, true); + + if (svc.Service_Name.empty() || svc.Service_Path.empty()) { + // INVALID SERVICE ENTRY + } else { + beg = svc.Service_Path.find_last_of("/"); + if (beg == string::npos) + svc.Service_Binary = svc.Service_Path; + else + svc.Service_Binary = svc.Service_Path.substr(beg + 1); + +/* +#ifdef _USING_SHORT_SERVICE_NAMES + if (svc.Service_Name.length() > 16) { + LOGERROR("WARNING: Ignoring service %s (-> %s)\n" + " defined in %s is greater than 16 characters and will\n" + " not be able to be run by init on android-7.1 or below!\n", + svc.Service_Name.c_str(), svc.Service_Path.c_str(), rc_file.c_str() + ); + } + else +#endif +*/ + RC_Services.push_back(svc); + } + } + real_line.clear(); + } + file.close(); + + for (size_t i = 0; i < imports.size(); ++i) { + Parse_RC_File(imports[i], RC_Services); + } +} + +vector Get_List_Of_Additional_Services(void) { vector services; + // Additional Services needed by vold_decrypt (eg qseecomd, hwservicemanager, etc) vector service_names = TWFunc::Split_String(TW_CRYPTO_SYSTEM_VOLD_SERVICES, " "); - for (size_t i = 0; i < service_names.size(); ++i) { AdditionalService svc; - svc.service_name = service_names[i]; + svc.Service_Name = service_names[i]; + svc.exists = false; services.push_back(svc); + +#ifdef _USING_SHORT_SERVICE_NAMES + // Fallback code for >16 character service names which + // allows for multiple definitions in custom .rc files + if (service_names[i].length() > 12) { + svc.Service_Name = service_names[i].substr(0, 12); // 16-4(prefix)=12 + svc.exists = false; + services.push_back(svc); + } +#endif } + // Read list of all services defined in all .rc files + vector RC_Services; + Parse_RC_File("/init.rc", RC_Services); + + + // Cross reference Additional Services against the .rc Services and establish + // availability of the binaries, otherwise disable it to avoid unnecessary + // delays and log spam. + // Also check for duplicate entries between TWRP and vold_decrypt so we can + // stop and restart any conflicting services. + for (size_t i = 0; i < RC_Services.size(); ++i) { + string prefix = RC_Services[i].Service_Name.substr(0, 4); + +#ifdef _USING_SHORT_SERVICE_NAMES + map rc_indeces; +#endif + + if (prefix != "sys_" && prefix != "ven_") { +#ifdef _USING_SHORT_SERVICE_NAMES + if (RC_Services[i].Service_Name.length() > 12) { + // save this entry for potential binary name match + rc_indeces.insert(pair(RC_Services[i].Service_Binary, i)); + } +#endif + continue; + } + + for (size_t j = 0; j < services.size(); ++j) { + string path = RC_Services[i].Service_Path; + if (prefix == "ven_" && will_VendorBin_Be_Symlinked()) { + path = "/system" + path; // vendor is going to get symlinked to /system/vendor + } + + if (RC_Services[i].Service_Name == prefix + services[j].Service_Name) { + if (!services[j].VOLD_Service_Name.empty() && TWFunc::Path_Exists(path)) { + // Duplicate match, log but use previous definition + LOGERROR("Service %s: VOLD_Service_Name already defined as %s\n", RC_Services[i].Service_Name.c_str(), services[j].VOLD_Service_Name.c_str()); + } + else if (TWFunc::Path_Exists(path)) { + services[j].exists = true; + services[j].VOLD_Service_Name = RC_Services[i].Service_Name; // prefix + service_name + services[j].Service_Path = RC_Services[i].Service_Path; + services[j].Service_Binary = RC_Services[i].Service_Binary; + + if (Service_Exists(services[j].Service_Name)) + services[j].TWRP_Service_Name = services[j].Service_Name; + else if (Service_Exists("sbin" + services[j].Service_Name)) + services[j].TWRP_Service_Name = "sbin" + services[j].Service_Name; + else + services[j].TWRP_Service_Name.clear(); + +#ifdef _USING_SHORT_SERVICE_NAMES + if (services[j].TWRP_Service_Name.empty()) { + // Try matching Service_Binary (due to 16 character service_name limit in 7.1 and below) + map::iterator it = rc_indeces.find(services[j].Service_Binary); + if (it != rc_indeces.end()) { + services[j].TWRP_Service_Name = RC_Services[it->second].Service_Name; + } + } +#endif + } + break; + } + } + } + + LOGINFO("List of additional services for vold_decrypt:\n"); + for (size_t i = 0; i < services.size(); ++i) { + if (services[i].exists) { + if (services[i].TWRP_Service_Name.empty()) { + LOGINFO(" %s: Enabled as %s -> %s\n", + services[i].Service_Name.c_str(), + services[i].VOLD_Service_Name.c_str(), services[i].Service_Path.c_str() + ); + } else { + LOGINFO(" %s: Enabled as %s -> %s (temporarily replacing TWRP's %s service)\n", + services[i].Service_Name.c_str(), + services[i].VOLD_Service_Name.c_str(), services[i].Service_Path.c_str(), + services[i].TWRP_Service_Name.c_str() + ); + } + } + else + LOGINFO(" %s: Disabled due to lack of matching binary\n", services[i].Service_Name.c_str()); + } return services; } #endif -int vold_decrypt(string Password) -{ + +/* Misc Functions */ +void Set_Needed_Properties(void) { + // vold won't start without ro.storage_structure on Kitkat + string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk"); + int sdkver = 20; + if (!sdkverstr.empty()) { + sdkver = atoi(sdkverstr.c_str()); + } + if (sdkver <= 19) { + string ro_storage_structure = TWFunc::System_Property_Get("ro.storage_structure"); + if (!ro_storage_structure.empty()) + property_set("ro.storage_structure", ro_storage_structure.c_str()); + } +} + + +/* vdc Functions */ +typedef struct { + string Output; // Entire line excluding \n + int ResponseCode; // ResponseCode.h (int) + int Sequence; // Sequence (int) + int Message; // Message (string) but we're only interested in int +} vdc_ReturnValues; + +int Exec_vdc_cryptfs(const string& command, const string& argument, vdc_ReturnValues* vdcResult) { + pid_t pid; + int status; + int pipe_fd[2]; + vdcResult->Output.clear(); + + if (pipe(pipe_fd)) { + LOGERROR("exec_vdc_cryptfs: pipe() error!\n"); + return -1; + } + + const char *cmd[] = { "/system/bin/vdc", "cryptfs" }; + const char *env[] = { "LD_LIBRARY_PATH=/system/lib64:/system/lib", NULL }; + +#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG + string log_name = "/tmp/strace_vdc_" + command; +#endif + + switch(pid = fork()) + { + case -1: + LOGERROR("exec_vdc_cryptfs: fork failed: %d (%s)!\n", errno, strerror(errno)); + return -1; + + case 0: // child + fflush(stdout); fflush(stderr); + close(pipe_fd[0]); + dup2(pipe_fd[1], STDOUT_FILENO); + dup2(pipe_fd[1], STDERR_FILENO); + close(pipe_fd[1]); + +#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG + if (has_strace) { + if (argument.empty()) + execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", log_name.c_str(), + "-E", env[0], cmd[0], cmd[1], command.c_str(), NULL); + else + execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", log_name.c_str(), + "-E", env[0], cmd[0], cmd[1], command.c_str(), argument.c_str(), NULL); + } else +#endif + if (argument.empty()) + execle(cmd[0], cmd[0], cmd[1], command.c_str(), NULL, env); + else + execle(cmd[0], cmd[0], cmd[1], command.c_str(), argument.c_str(), NULL, env); + _exit(127); + break; + + default: + { + int timeout = 30*100; + vdcResult->Output.clear(); + close(pipe_fd[1]); + + // Non-blocking read loop with timeout + int flags = fcntl(pipe_fd[0], F_GETFL, 0); + fcntl(pipe_fd[0], F_SETFL, flags | O_NONBLOCK); + + char buffer[128]; + ssize_t count; + pid_t retpid = waitpid(pid, &status, WNOHANG); + while (true) { + count = read(pipe_fd[0], buffer, sizeof(buffer)); + if (count == -1) { + if (errno == EINTR) + continue; + else if (errno != EAGAIN) + LOGERROR("exec_vdc_cryptfs: read() error %d (%s)\n!", errno, strerror(errno)); + } else if (count > 0) { + vdcResult->Output.append(buffer, count); + } + + retpid = waitpid(pid, &status, WNOHANG); + if (retpid == 0 && --timeout) + usleep(10000); + else + break; + }; + close(pipe_fd[0]); + if (vdcResult->Output.length() > 0 && vdcResult->Output[vdcResult->Output.length() - 1] == '\n') + vdcResult->Output.erase(vdcResult->Output.length() - 1); + vdcResult->ResponseCode = vdcResult->Sequence = vdcResult->Message = -1; + sscanf(vdcResult->Output.c_str(), "%d %d %d", &vdcResult->ResponseCode, &vdcResult->Sequence, &vdcResult->Message); + + // Error handling + if (retpid == 0 && timeout == 0) { + LOGERROR("exec_vdc_cryptfs: took too long, killing process\n"); + kill(pid, SIGKILL); + for (timeout = 5; retpid == 0 && timeout; --timeout) { + sleep(1); + retpid = waitpid(pid, &status, WNOHANG); + } + if (retpid) + LOGINFO("exec_vdc_cryptfs: process killed successfully\n"); + else + LOGERROR("exec_vdc_cryptfs: process took too long to kill, may be a zombie process\n"); + return VD_ERR_VOLD_OPERATION_TIMEDOUT; + } else if (retpid > 0) { + if (WIFSIGNALED(status)) { + LOGERROR("exec_vdc_cryptfs: process ended with signal: %d\n", WTERMSIG(status)); // Seg fault or some other non-graceful termination + return -1; + } + } else if (retpid < 0) { // no PID returned + if (errno == ECHILD) + LOGINFO("exec_vdc_cryptfs: no child process exist\n"); + else { + LOGERROR("exec_vdc_cryptfs: Unexpected error %d (%s)\n", errno, strerror(errno)); + return -1; + } + } + return 0; + } + } +} + +int Run_vdc(const string& Password) { + int res; + struct timeval t1, t2; + vdc_ReturnValues vdcResult; + + LOGINFO("About to run vdc...\n"); + + // Wait for vold connection + gettimeofday(&t1, NULL); + t2 = t1; + while ((t2.tv_sec - t1.tv_sec) < 5) { + // cryptfs getpwtype returns: R1=213(PasswordTypeResult) R2=? R3="password", "pattern", "pin", "default" + res = Exec_vdc_cryptfs("getpwtype", "", &vdcResult); + if (vdcResult.ResponseCode == PASSWORD_TYPE_RESULT) { + char str_res[4 + sizeof(int) + 1]; + snprintf(str_res, sizeof(str_res), "ret=%d", res); + vdcResult.Output += str_res; + res = 0; + break; + } + LOGINFO("Retrying connection to vold (Reason: %s)\n", vdcResult.Output.c_str()); + usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts + gettimeofday(&t2, NULL); + } + + if (res == 0 && (t2.tv_sec - t1.tv_sec) < 5) + LOGINFO("Connected to vold (%s)\n", vdcResult.Output.c_str()); + else if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT) + return VD_ERR_VOLD_OPERATION_TIMEDOUT; // should never happen for getpwtype + else if (res) + return VD_ERR_FORK_EXECL_ERROR; + else if (vdcResult.ResponseCode != -1) + return VD_ERR_VOLD_UNEXPECTED_RESPONSE; + else + return VD_ERR_VDC_FAILED_TO_CONNECT; + + + // Input password from GUI, or default password + res = Exec_vdc_cryptfs("checkpw", Password, &vdcResult); + if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT) + return VD_ERR_VOLD_OPERATION_TIMEDOUT; + else if (res) + return VD_ERR_FORK_EXECL_ERROR; + + LOGINFO("vdc cryptfs result (passwd): %s (ret=%d)\n", vdcResult.Output.c_str(), res); + /* + if (res == 0 && vdcResult.ResponseCode != COMMAND_OKAY) + return VD_ERR_VOLD_UNEXPECTED_RESPONSE; + */ + + if (vdcResult.Message != 0) { + // try falling back to Lollipop hex passwords + string hexPassword = convert_key_to_hex_ascii(Password); + res = Exec_vdc_cryptfs("checkpw", hexPassword, &vdcResult); + if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT) + return VD_ERR_VOLD_OPERATION_TIMEDOUT; + else if (res) + return VD_ERR_FORK_EXECL_ERROR; + + LOGINFO("vdc cryptfs result (hex_pw): %s (ret=%d)\n", vdcResult.Output.c_str(), res); + /* + if (res == 0 && vdcResult.ResponseCode != COMMAND_OKAY) + return VD_ERR_VOLD_UNEXPECTED_RESPONSE; + */ + } + + // vdc's return value is dependant upon source origin, it will either + // return 0 or ResponseCode, so disregard and focus on decryption instead + if (vdcResult.Message == 0) { + // Decryption successful wait for crypto blk dev + Wait_For_Property("ro.crypto.fs_crypto_blkdev"); + res = VD_SUCCESS; + } else if (vdcResult.ResponseCode != COMMAND_OKAY) { + res = VD_ERR_VOLD_UNEXPECTED_RESPONSE; + } else { + res = VD_ERR_DECRYPTION_FAILED; + } + + return res; +} + +int Vold_Decrypt_Core(const string& Password) { int res; - bool output_dmesg_to_log = false; bool is_vendor_symlinked = false; bool is_firmware_symlinked = false; + bool is_fstab_symlinked = false; bool is_vold_running = false; if (Password.empty()) { - LOGDECRYPT("vold_decrypt: password is empty!\n"); - return -1; + LOGINFO("vold_decrypt: password is empty!\n"); + return VD_ERR_PASSWORD_EMPTY; } // Mount system and check for vold and vdc if (!PartitionManager.Mount_By_Path("/system", true)) { - return -1; + return VD_ERR_UNABLE_TO_MOUNT_SYSTEM; } else if (!TWFunc::Path_Exists("/system/bin/vold")) { - LOGDECRYPT("ERROR: /system/bin/vold not found, aborting.\n"); - gui_msg(Msg(msg::kError, "decrypt_data_vold_os_missing=Missing files needed for vold decrypt: {1}")("/system/bin/vold")); - return -1; + LOGINFO("ERROR: /system/bin/vold not found, aborting.\n"); + return VD_ERR_MISSING_VOLD; } else if (!TWFunc::Path_Exists("/system/bin/vdc")) { - LOGDECRYPT("ERROR: /system/bin/vdc not found, aborting.\n"); - gui_msg(Msg(msg::kError, "decrypt_data_vold_os_missing=Missing files needed for vold decrypt: {1}")("/system/bin/vdc")); - return -1; + LOGINFO("ERROR: /system/bin/vdc not found, aborting.\n"); + return VD_ERR_MISSING_VDC; } fp_kmsg = fopen("/dev/kmsg", "a"); - LOGDECRYPT("TW_CRYPTO_USE_SYSTEM_VOLD := true\n"); - LOGDECRYPT("Attempting to use system's vold for decryption...\n"); + LOGINFO("TW_CRYPTO_USE_SYSTEM_VOLD := true\n"); -#ifndef TW_CRYPTO_SYSTEM_VOLD_DISABLE_TIMEOUT - has_timeout = TWFunc::Path_Exists("/sbin/timeout"); - if (!has_timeout) - LOGDECRYPT("timeout binary not found, disabling timeout in vold_decrypt!\n"); -#endif + // just cache the result to avoid unneeded duplicates in recovery.log + LOGINFO("Checking existence of vendor and firmware partitions...\n"); + is_Vendor_Mounted(); + is_Firmware_Mounted(); + + LOGINFO("Attempting to use system's vold for decryption...\n"); #ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG - has_strace = TWFunc::Path_Exists("/sbin/strace"); - if (!has_strace) - LOGDECRYPT("strace binary not found, disabling strace in vold_decrypt!\n"); - pid_t pid_strace = strace_init(); + Strace_init_Start(); #endif #ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES @@ -456,74 +1017,61 @@ int vold_decrypt(string Password) // Check if TWRP is running any of the services for (size_t i = 0; i < Services.size(); ++i) { - if (Service_Exists(Services[i].service_name)) - Services[i].twrp_svc_name = Services[i].service_name; - else if (Service_Exists("sbin" + Services[i].service_name)) - Services[i].twrp_svc_name = "sbin" + Services[i].service_name; - else - Services[i].twrp_svc_name.clear(); - - if (!Services[i].twrp_svc_name.empty() && !Is_Service_Stopped(Services[i].twrp_svc_name)) { + if (!Services[i].TWRP_Service_Name.empty() && !Is_Service_Stopped(Services[i].TWRP_Service_Name)) { Services[i].resume = true; - Stop_Service(Services[i].twrp_svc_name); + Stop_Service(Services[i].TWRP_Service_Name); } else Services[i].resume = false; - - // vold_decrypt system services have to be named sys_{service} in the .rc files - Services[i].service_name = "sys_" + Services[i].service_name; } #endif - LOGDECRYPT("Setting up folders and permissions...\n"); + LOGINFO("Setting up folders and permissions...\n"); + is_fstab_symlinked = Symlink_Recovery_Fstab(); is_vendor_symlinked = Symlink_Vendor_Folder(); is_firmware_symlinked = Symlink_Firmware_Folder(); Symlink_Firmware_Files(is_vendor_symlinked, is_firmware_symlinked); - set_needed_props(); + Set_Needed_Properties(); // Start services needed for vold decrypt - LOGDECRYPT("Starting services...\n"); + LOGINFO("Starting services...\n"); #ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES for (size_t i = 0; i < Services.size(); ++i) { - Services[i].is_running = Start_Service(Services[i].service_name); + if (Services[i].exists) + Services[i].is_running = Start_Service(Services[i].VOLD_Service_Name); } #endif is_vold_running = Start_Service("sys_vold"); if (is_vold_running) { - #ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES for (size_t i = 0; i < Services.size(); ++i) { - if (!Is_Service_Running(Services[i].service_name) && Services[i].resume) { + if (Services[i].exists && !Is_Service_Running(Services[i].VOLD_Service_Name) && Services[i].resume) { // if system_service has died restart the twrp_service - LOGDECRYPT("%s is not running, resuming %s!\n", Services[i].service_name.c_str(), Services[i].twrp_svc_name.c_str()); - Start_Service(Services[i].twrp_svc_name); + LOGINFO("%s is not running, resuming %s!\n", Services[i].VOLD_Service_Name.c_str(), Services[i].TWRP_Service_Name.c_str()); + Start_Service(Services[i].TWRP_Service_Name); } } #endif - - res = run_vdc(Password); + res = Run_vdc(Password); if (res != 0) { - // Decryption was unsuccessful - LOGDECRYPT("Decryption failed\n"); - output_dmesg_to_log = true; + LOGINFO("Decryption failed\n"); } } else { - LOGDECRYPT("Failed to start vold\n"); - TWFunc::Exec_Cmd("echo \"$(getprop | grep init.svc)\" >> /dev/kmsg"); - output_dmesg_to_log = true; + LOGINFO("Failed to start vold\n"); + res = VD_ERR_VOLD_FAILED_TO_START; } // Stop services needed for vold decrypt so /system can be unmounted - LOGDECRYPT("Stopping services...\n"); + LOGINFO("Stopping services...\n"); Stop_Service("sys_vold"); #ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES for (size_t i = 0; i < Services.size(); ++i) { - if (!Is_Service_Running(Services[i].service_name) && Services[i].resume) - Stop_Service(Services[i].twrp_svc_name); - else - Stop_Service(Services[i].service_name); + if (!Is_Service_Running(Services[i].VOLD_Service_Name) && Services[i].resume) + Stop_Service(Services[i].TWRP_Service_Name); + else if (Services[i].exists) + Stop_Service(Services[i].VOLD_Service_Name); } #endif @@ -531,52 +1079,28 @@ int vold_decrypt(string Password) Restore_Firmware_Folder(); if (is_vendor_symlinked) Restore_Vendor_Folder(); + if (is_fstab_symlinked) + Restore_Recovery_Fstab(); if (!PartitionManager.UnMount_By_Path("/system", true)) { // PartitionManager failed to unmount /system, this should not happen, // but in case it does, do a lazy unmount - LOGDECRYPT("WARNING: system could not be unmounted normally!\n"); - TWFunc::Exec_Cmd("umount -l /system"); + LOGINFO("WARNING: system could not be unmounted normally!\n"); + umount2("/system", MNT_DETACH); } - LOGDECRYPT("Finished.\n"); + LOGINFO("Finished.\n"); #ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES // Restart previously running services for (size_t i = 0; i < Services.size(); ++i) { if (Services[i].resume) - Start_Service(Services[i].twrp_svc_name); + Start_Service(Services[i].TWRP_Service_Name); } #endif #ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG - if (pid_strace > 0) { - LOGDECRYPT_KMSG("Stopping strace_init (pid=%d)\n", pid_strace); - int timeout; - int status; - pid_t retpid = waitpid(pid_strace, &status, WNOHANG); - - kill(pid_strace, SIGTERM); - for (timeout = 5; retpid == 0 && timeout; --timeout) { - sleep(1); - retpid = waitpid(pid_strace, &status, WNOHANG); - } - if (retpid) - LOGDECRYPT_KMSG("strace_init terminated successfully\n"); - else { - // SIGTERM didn't work, kill it instead - kill(pid_strace, SIGKILL); - for (timeout = 5; retpid == 0 && timeout; --timeout) { - sleep(1); - retpid = waitpid(pid_strace, &status, WNOHANG); - } - if (retpid) - LOGDECRYPT_KMSG("strace_init killed successfully\n"); - else - LOGDECRYPT_KMSG("strace_init took too long to kill, may be a zombie process\n"); - } - } - output_dmesg_to_log = true; + Strace_init_Stop(); #endif // Finish up and exit @@ -585,14 +1109,18 @@ int vold_decrypt(string Password) fclose(fp_kmsg); } - if (output_dmesg_to_log) - output_dmesg_to_recoverylog(); + return res; +} - // Finally check if crypto device is up - if (wait_for_property("ro.crypto.fs_crypto_blkdev", 0) != "error") - res = 0; - else - res = -1; +} // namespace - return res; +/* + * Common vold Response Codes / Errors: + * 406 (OpFailedStorageNotFound) -> Problem reading or parsing fstab + * + */ + +/* Main function separated from core in case we want to return error info */ +int vold_decrypt(const string& Password) { + return Vold_Decrypt_Core(Password); } diff --git a/crypto/vold_decrypt/vold_decrypt.h b/crypto/vold_decrypt/vold_decrypt.h index 70db404e9..ba7a74720 100644 --- a/crypto/vold_decrypt/vold_decrypt.h +++ b/crypto/vold_decrypt/vold_decrypt.h @@ -21,6 +21,22 @@ #include -int vold_decrypt(std::string Password); +// -_- +enum { + VD_SUCCESS = 0, + VD_ERR_DECRYPTION_FAILED = -1, + VD_ERR_UNABLE_TO_MOUNT_SYSTEM = -2, + VD_ERR_MISSING_VOLD = -3, + VD_ERR_MISSING_VDC = -4, + VD_ERR_VDC_FAILED_TO_CONNECT = -5, + VD_ERR_VOLD_FAILED_TO_START = -6, + VD_ERR_VOLD_UNEXPECTED_RESPONSE = -7, + VD_ERR_VOLD_OPERATION_TIMEDOUT = -8, + VD_ERR_FORK_EXECL_ERROR = -9, + VD_ERR_PASSWORD_EMPTY = -10, +}; + + +int vold_decrypt(const std::string& Password); #endif // _VOLD_DECRYPT_H diff --git a/partitionmanager.cpp b/partitionmanager.cpp index 54ce6912d..7adcc9f46 100644 --- a/partitionmanager.cpp +++ b/partitionmanager.cpp @@ -1658,6 +1658,16 @@ int TWPartitionManager::Decrypt_Device(string Password) { #ifdef TW_CRYPTO_USE_SYSTEM_VOLD if (pwret != 0) { pwret = vold_decrypt(Password); + switch (pwret) { + case VD_SUCCESS: + break; + case VD_ERR_MISSING_VDC: + gui_msg(Msg(msg::kError, "decrypt_data_vold_os_missing=Missing files needed for vold decrypt: {1}")("/system/bin/vdc")); + break; + case VD_ERR_MISSING_VOLD: + gui_msg(Msg(msg::kError, "decrypt_data_vold_os_missing=Missing files needed for vold decrypt: {1}")("/system/bin/vold")); + break; + } } #endif // TW_CRYPTO_USE_SYSTEM_VOLD -- cgit v1.2.3