diff --git a/.clang-format b/.clang-format index 0e0f4d143e..4a3bd2fc32 100644 --- a/.clang-format +++ b/.clang-format @@ -1,3 +1,33 @@ +# bootable/recovery project uses repohook to apply `clang-format` to the changed lines, with the +# local style file in `.clang-format`. This will be triggered automatically with `repo upload`. +# Alternatively, one can stage and format a change with `git clang-format` directly. +# +# $ git add +# $ git clang-format --style file +# +# Or to format a committed change. +# +# $ git clang-format --style file HEAD~1 +# +# `--style file` will pick up the local style file in `.clang-format`. This can be configured as the +# default behavior for bootable/recovery project. +# +# $ git config --local clangFormat.style file +# +# Note that `repo upload` calls the `clang-format` binary in Android repo (i.e. +# `$ANDROID_BUILD_TOP/prebuilts/clang/host/linux-x86/clang-stable/bin/clang-format`), which might +# give slightly different results from the one installed in host machine (e.g. +# `/usr/bin/clang-format`). Specifying the file with `--binary` will ensure consistent results. +# +# $ git clang-format --binary \ +# /path/to/aosp-master/prebuilts/clang/host/linux-x86/clang-stable/bin/clang-format +# +# Or to do one-time setup to make it default. +# +# $ git config --local clangFormat.binary \ +# /path/to/aosp-master/prebuilts/clang/host/linux-x86/clang-stable/bin/clang-format +# + BasedOnStyle: Google AllowShortBlocksOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty diff --git a/.gitignore b/.gitignore index e03babb1f6..480f5478d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .*.swp *~ tags +.vscode diff --git a/Android.bp b/Android.bp old mode 100644 new mode 100755 index 8837b202b5..c9f92b6be4 --- a/Android.bp +++ b/Android.bp @@ -6,3 +6,15 @@ subdirs = [ // "otautil", // "uncrypt", ] + +cc_defaults { + name: "recovery_defaults", + cflags: [ + "-D_FILE_OFFSET_BITS=64", + // Must be the same as RECOVERY_API_VERSION. + "-DRECOVERY_API_VERSION=3", + "-Wall", + "-Werror", + ], + cpp_std: "c++17", +} diff --git a/Android.mk b/Android.mk index d0d962f54c..0f454f212b 100755 --- a/Android.mk +++ b/Android.mk @@ -36,11 +36,6 @@ endif ifeq ($(PROJECT_PATH_AGREES),true) -ifneq (,$(filter $(PLATFORM_SDK_VERSION), 21 22)) -# Make recovery domain permissive for TWRP - BOARD_SEPOLICY_UNION += twrp.te -endif - ifeq ($(CM_PLATFORM_SDK_VERSION),) CM_PLATFORM_SDK_VERSION := 0 endif @@ -52,6 +47,9 @@ TWHTCD_PATH := $(TWRES_PATH)htcd/ TARGET_RECOVERY_GUI := true +LOCAL_STATIC_LIBRARIES := +LOCAL_SHARED_LIBRARIES := + ifneq ($(TW_DEVICE_VERSION),) LOCAL_CFLAGS += -DTW_DEVICE_VERSION='"-$(TW_DEVICE_VERSION)"' else @@ -70,12 +68,27 @@ LOCAL_SRC_FILES := \ partition.cpp \ partitionmanager.cpp \ progresstracking.cpp \ - twinstall.cpp \ + startupArgs.cpp \ twrp-functions.cpp \ twrpDigestDriver.cpp \ openrecoveryscript.cpp \ tarWrite.c \ - twrpAdbBuFifo.cpp + twrpAdbBuFifo.cpp \ + twrpApex.cpp \ + twrpRepacker.cpp + +LOCAL_STATIC_LIBRARIES += libavb libtwrpinstall +LOCAL_SHARED_LIBRARIES += libfs_mgr libinit +LOCAL_C_INCLUDES += \ + 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/extras/ext4_utils/include \ + $(LOCAL_PATH)/twinstall/include ifneq ($(TARGET_RECOVERY_REBOOT_SRC),) LOCAL_SRC_FILES += $(TARGET_RECOVERY_REBOOT_SRC) @@ -83,85 +96,35 @@ endif LOCAL_MODULE := recovery -#LOCAL_FORCE_STATIC_EXECUTABLE := true - -#ifeq ($(TARGET_USERIMAGES_USE_F2FS),true) -#ifeq ($(HOST_OS),linux) -#LOCAL_REQUIRED_MODULES := mkfs.f2fs -#endif -#endif - 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_STATIC_LIBRARIES := \ -# libext4_utils_static \ -# libsparse_static \ -# libminzip \ -# libz \ -# libmtdutils \ -# libmincrypt \ -# libminadbd \ -# libminui \ -# libpixelflinger_static \ -# libpng \ -# libfs_mgr \ -# libcutils \ -# liblog \ -# libselinux \ -# libstdc++ \ -# libm \ -# libc - LOCAL_C_INCLUDES += \ + bionic \ system/vold \ system/extras \ system/core/adb \ system/core/libsparse \ external/zlib \ - $(LOCAL_PATH)/bootloader_message_twrp/include - -LOCAL_C_INCLUDES += bionic -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) - LOCAL_C_INCLUDES += external/stlport/stlport external/openssl/include - LOCAL_CFLAGS += -DUSE_FUSE_SIDELOAD22 -else - LOCAL_C_INCLUDES += external/boringssl/include external/libcxx/include -endif - -LOCAL_STATIC_LIBRARIES := -LOCAL_SHARED_LIBRARIES := + system/core/libpixelflinger/include \ + system/core/libziparchive/include \ + external/freetype/include \ + external/boringssl/include \ + external/libcxx/include \ + external/libselinux/include \ + $(LOCAL_PATH)/recovery_ui/include \ + $(LOCAL_PATH)/otautil/include \ + $(LOCAL_PATH)/install/include \ + $(LOCAL_PATH)/fuse_sideload/include \ + $(LOCAL_PATH)/install/include \ + $(LOCAL_PATH)/twrpinstall/include LOCAL_STATIC_LIBRARIES += libguitwrp -LOCAL_SHARED_LIBRARIES += libaosprecovery libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libtwadbbu libbootloader_message_twrp -LOCAL_SHARED_LIBRARIES += libcrecovery libtwadbbu libtwrpdigest libc++ - -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) - LOCAL_SHARED_LIBRARIES += libstlport - LOCAL_CFLAGS += -DTW_NO_SHA2_LIBRARY -endif -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0) - LOCAL_SHARED_LIBRARIES += libmincrypttwrp - LOCAL_C_INCLUDES += $(LOCAL_PATH)/libmincrypt/includes - LOCAL_CFLAGS += -DUSE_OLD_VERIFIER -else - LOCAL_SHARED_LIBRARIES += libcrypto -endif - -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0) - LOCAL_SHARED_LIBRARIES += libbase -endif - -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) - LOCAL_SHARED_LIBRARIES += libziparchive - LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include -else - LOCAL_SHARED_LIBRARIES += libminzip - LOCAL_CFLAGS += -DUSE_MINZIP -endif +LOCAL_SHARED_LIBRARIES += libz libc libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libtwadbbu libbootloader_message +LOCAL_SHARED_LIBRARIES += libcrecovery libtwrpdigest libc++ libaosprecovery libinit libcrypto libbase libziparchive libselinux ifneq ($(wildcard system/core/libsparse/Android.mk),) LOCAL_SHARED_LIBRARIES += libsparse @@ -172,34 +135,36 @@ ifeq ($(TW_OEM_BUILD),true) BOARD_HAS_NO_REAL_SDCARD := true TW_USE_TOOLBOX := true TW_EXCLUDE_MTP := true + TW_EXCLUDE_TZDATA := true + TW_EXCLUDE_NANO := true + TW_EXCLUDE_BASH := true endif -ifeq ($(TARGET_USERIMAGES_USE_EXT4), true) - ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 28; echo $$?),0) - LOCAL_CFLAGS += -DUSE_EXT4 - LOCAL_C_INCLUDES += system/extras/ext4_utils - LOCAL_SHARED_LIBRARIES += libext4_utils - ifneq ($(wildcard external/lz4/Android.mk),) - #LOCAL_STATIC_LIBRARIES += liblz4 - endif - endif -endif -LOCAL_C_INCLUDES += external/libselinux/include -LOCAL_SHARED_LIBRARIES += libselinux - ifeq ($(AB_OTA_UPDATER),true) LOCAL_CFLAGS += -DAB_OTA_UPDATER=1 LOCAL_SHARED_LIBRARIES += libhardware android.hardware.boot@1.0 - TWRP_REQUIRED_MODULES += libhardware + TWRP_REQUIRED_MODULES += libhardware android.hardware.boot@1.0-service android.hardware.boot@1.0-service.rc +endif + +ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) + LOCAL_CFLAGS += -DPRODUCT_USE_DYNAMIC_PARTITIONS=1 + TWRP_REQUIRED_MODULES += android.hardware.health@2.0-service android.hardware.health@2.0-service.rc +endif + +ifeq ($(TW_USES_VENDOR_LIBS),true) + LOCAL_CFLAGS += -DUSE_VENDOR_LIBS=1 +endif + +ifneq ($(BOARD_SUPER_PARTITION_PARTITION_LIST),) + LOCAL_CFLAGS += "-DBOARD_SUPER_PARTITION_PARTITION_LIST=\"$(shell echo $(BOARD_SUPER_PARTITION_PARTITION_LIST) | sed -r 's/\b(.)/\u\1/g' | sed -e 's/ \+/, /g')\"" +endif + +ifeq ($(TW_NO_BIND_SYSTEM),true) + LOCAL_CFLAGS += -DTW_NO_BIND_SYSTEM endif -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin -#ifeq ($(TARGET_RECOVERY_UI_LIB),) -# LOCAL_SRC_FILES += default_device.cpp -#else -# LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) -#endif ifeq ($(TARGET_RECOVERY_TWRP_LIB),) LOCAL_SRC_FILES += BasePartition.cpp else @@ -214,15 +179,13 @@ ifeq ($(shell git -C $(LOCAL_PATH) diff --quiet; echo $$?),1) endif LOCAL_CFLAGS += -DTW_GIT_REVISION='"$(tw_git_revision)"' -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) -ifeq ($(TW_EXCLUDE_MTP),) - LOCAL_SHARED_LIBRARIES += libtwrpmtp-ffs -endif +ifeq ($(TW_FORCE_USE_BUSYBOX), true) + TW_USE_TOOLBOX := false else -ifeq ($(TW_EXCLUDE_MTP),) - LOCAL_CFLAGS += -DTW_HAS_LEGACY_MTP - LOCAL_SHARED_LIBRARIES += libtwrpmtp-legacy + TW_USE_TOOLBOX := true endif +ifeq ($(TW_EXCLUDE_MTP),) + LOCAL_SHARED_LIBRARIES += libtwrpmtp-ffs endif #TWRP Build Flags @@ -320,19 +283,18 @@ ifeq ($(TW_INCLUDE_L_CRYPTO), true) TW_INCLUDE_CRYPTO := true endif ifeq ($(TW_INCLUDE_CRYPTO), true) - LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO + LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO -DUSE_FSCRYPT -Wno-macro-redefined LOCAL_SHARED_LIBRARIES += libcryptfsfde libgpt_twrp - LOCAL_C_INCLUDES += external/boringssl/src/include - 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 28; echo $$?),0) - LOCAL_CFLAGS += -DTW_INCLUDE_FBE_METADATA_DECRYPT - endif - endif + LOCAL_C_INCLUDES += external/boringssl/src/include bootable/recovery/crypto/fscrypt \ + bootable/recovery/crypto + TW_INCLUDE_CRYPTO_FBE := true + LOCAL_CFLAGS += -DTW_INCLUDE_FBE + LOCAL_SHARED_LIBRARIES += libtwrpfscrypt android.frameworks.stats@1.0 android.hardware.authsecret@1.0 \ + android.hardware.oemlock@1.0 + LOCAL_CFLAGS += -DTW_INCLUDE_FBE_METADATA_DECRYPT ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),) ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false) + TW_INCLUDE_LIBRESETPROP := true LOCAL_CFLAGS += -DTW_CRYPTO_USE_SYSTEM_VOLD LOCAL_STATIC_LIBRARIES += libvolddecrypt endif @@ -387,9 +349,7 @@ endif ifneq ($(TARGET_RECOVERY_INITRC),) TW_EXCLUDE_DEFAULT_USB_INIT := true endif -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0) - LOCAL_CFLAGS += -DTW_USE_NEW_MINADBD -endif +LOCAL_CFLAGS += -DTW_USE_NEW_MINADBD ifneq ($(TW_DEFAULT_LANGUAGE),) LOCAL_CFLAGS += -DTW_DEFAULT_LANGUAGE=$(TW_DEFAULT_LANGUAGE) else @@ -398,51 +358,104 @@ endif ifneq ($(TW_CLOCK_OFFSET),) LOCAL_CFLAGS += -DTW_CLOCK_OFFSET=$(TW_CLOCK_OFFSET) endif +ifneq ($(TW_OVERRIDE_SYSTEM_PROPS),) + TW_INCLUDE_LIBRESETPROP := true + LOCAL_CFLAGS += -DTW_OVERRIDE_SYSTEM_PROPS=$(TW_OVERRIDE_SYSTEM_PROPS) +endif +ifneq ($(TW_INCLUDE_LIBRESETPROP),) + LOCAL_SHARED_LIBRARIES += libresetprop + LOCAL_C_INCLUDES += external/magisk-prebuilt/include + LOCAL_CFLAGS += -DTW_INCLUDE_LIBRESETPROP +endif +ifeq ($(TW_EXCLUDE_NANO), true) + LOCAL_CFLAGS += -DTW_EXCLUDE_NANO +endif + TWRP_REQUIRED_MODULES += \ + relink_libraries \ + relink_binaries \ + twrp_ramdisk \ dump_image \ erase_image \ flash_image \ mke2fs.conf \ pigz \ teamwin \ - toolbox_symlinks \ twrp \ fsck.fat \ fatlabel \ mkfs.fat \ permissive.sh \ simg2img_twrp \ - libbootloader_message_twrp \ + libbootloader_message \ init.recovery.hlthchrg.rc \ - init.recovery.service.rc + init.recovery.service.rc \ + init.recovery.ldconfig.rc \ + awk \ + toybox \ + toolbox \ + mkshrc_twrp \ + plat_hwservice_contexts \ + vendor_hwservice_contexts \ + minadbd \ + twrpbu \ + me.twrp.twrpapp.apk \ + privapp-permissions-twrpapp.xml + +ifneq ($(TW_EXCLUDE_TZDATA), true) +TWRP_REQUIRED_MODULES += \ + tzdata_twrp +endif -ifneq ($(TARGET_ARCH), arm64) - ifneq ($(TARGET_ARCH), x86_64) - LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker - else - LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 - endif -else - LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 +ifneq ($(TW_EXCLUDE_NANO), true) +TWRP_REQUIRED_MODULES += \ + nano_twrp \ + nano.rc endif -ifneq ($(TW_USE_TOOLBOX), true) - ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0) - LOCAL_POST_INSTALL_CMD := \ - $(hide) mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/sbin && \ - ln -sf /sbin/busybox $(TARGET_RECOVERY_ROOT_OUT)/sbin/sh - endif -else - ifneq ($(wildcard external/toybox/Android.mk),) - TWRP_REQUIRED_MODULES += toybox_symlinks - endif - ifneq ($(wildcard external/zip/Android.mk),) - TWRP_REQUIRED_MODULES += zip + +ifneq ($(TW_EXCLUDE_BASH), true) + ifneq ($(wildcard external/bash/.),) + TWRP_REQUIRED_MODULES += \ + bash_twrp endif - ifneq ($(wildcard external/unzip/Android.mk),) - TWRP_REQUIRED_MODULES += unzip +endif + +ifeq ($(TW_INCLUDE_REPACKTOOLS), true) +TWRP_REQUIRED_MODULES += \ + magiskboot +endif + +ifeq ($(TW_INCLUDE_RESETPROP), true) +TWRP_REQUIRED_MODULES += \ + resetprop +endif + +TWRP_REQUIRED_MODULES += \ + hwservicemanager \ + hwservicemanager.rc \ + vndservicemanager \ + vndservicemanager.rc + +ifneq ($(TW_INCLUDE_CRYPTO),) +TWRP_REQUIRED_MODULES += \ + vold_prepare_subdirs \ + task_recovery_profiles.json \ + fscryptpolicyget + ifneq ($(TW_INCLUDE_CRYPTO_FBE),) + TWRP_REQUIRED_MODULES += \ + plat_service_contexts \ + servicemanager \ + servicemanager.rc endif endif +ifneq ($(wildcard external/zip/Android.mk),) + TWRP_REQUIRED_MODULES += zip +endif +ifneq ($(wildcard external/unzip/Android.mk),) + TWRP_REQUIRED_MODULES += unzip +endif + ifneq ($(TW_NO_EXFAT), true) TWRP_REQUIRED_MODULES += mkexfatfs fsckexfat ifneq ($(TW_NO_EXFAT_FUSE), true) @@ -450,11 +463,7 @@ ifneq ($(TW_NO_EXFAT), true) endif endif ifeq ($(BOARD_HAS_NO_REAL_SDCARD),) - ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0) - TWRP_REQUIRED_MODULES += sgdisk - else - TWRP_REQUIRED_MODULES += sgdisk_static - endif + TWRP_REQUIRED_MODULES += sgdisk endif ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true) TWRP_REQUIRED_MODULES += openaes openaes_license @@ -480,7 +489,7 @@ ifneq ($(TW_EXCLUDE_DEFAULT_USB_INIT), true) TWRP_REQUIRED_MODULES += init.recovery.usb.rc endif ifeq ($(TWRP_INCLUDE_LOGCAT), true) - TWRP_REQUIRED_MODULES += logcat + TWRP_REQUIRED_MODULES += logcat event-log-tags ifeq ($(TARGET_USES_LOGD), true) TWRP_REQUIRED_MODULES += logd libsysutils libnl init.recovery.logd.rc endif @@ -492,57 +501,25 @@ endif LOCAL_CFLAGS += -DTWRES=\"$(TWRES_PATH)\" LOCAL_CFLAGS += -DTWHTCD_PATH=\"$(TWHTCD_PATH)\" ifeq ($(TW_INCLUDE_NTFS_3G),true) -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0) TWRP_REQUIRED_MODULES += \ mount.ntfs \ fsck.ntfs \ mkfs.ntfs -else - TWRP_REQUIRED_MODULES += \ - ntfs-3g \ - ntfsfix \ - mkntfs -endif endif ifeq ($(TARGET_USERIMAGES_USE_F2FS), true) -ifeq ($(shell test $(CM_PLATFORM_SDK_VERSION) -ge 3; echo $$?),0) - TWRP_REQUIRED_MODULES += \ - fsck.f2fs \ - mkfs.f2fs -endif -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) - TWRP_REQUIRED_MODULES += sload.f2fs -endif + TWRP_REQUIRED_MODULES += sload.f2fs \ + libfs_mgr \ + fs_mgr \ + libinit endif -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) - TWRP_REQUIRED_MODULES += ld.config.txt - ifeq ($(BOARD_VNDK_RUNTIME_DISABLE),true) - LOCAL_POST_INSTALL_CMD += \ - sed '0,/^namespace.default.search.paths\s\{1,\}/!b;//a\namespace.default.search.paths += \/sbin' \ - $(TARGET_OUT_ETC)/ld.config.vndk_lite.txt > $(TARGET_RECOVERY_ROOT_OUT)/sbin/ld.config.txt; - else - LOCAL_POST_INSTALL_CMD += \ - sed '0,/^namespace.default.search.paths\s\{1,\}/!b;//a\namespace.default.search.paths += \/sbin' \ - $(TARGET_OUT_ETC)/ld.config.txt > $(TARGET_RECOVERY_ROOT_OUT)/sbin/ld.config.txt; - endif -endif +TWRP_REQUIRED_MODULES += file_contexts_text -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 25; echo $$?),0) - TWRP_REQUIRED_MODULES += file_contexts_text +ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),) + TWRP_REQUIRED_MODULES += recovery-persist recovery-refresh endif -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0) - ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),) - TWRP_REQUIRED_MODULES += recovery-persist recovery-refresh - endif -endif - -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) - LOCAL_REQUIRED_MODULES += $(TWRP_REQUIRED_MODULES) -else - LOCAL_ADDITIONAL_DEPENDENCIES += $(TWRP_REQUIRED_MODULES) -endif +LOCAL_REQUIRED_MODULES += $(TWRP_REQUIRED_MODULES) include $(BUILD_EXECUTABLE) @@ -551,135 +528,40 @@ include $(CLEAR_VARS) LOCAL_MODULE := file_contexts_text LOCAL_MODULE_TAGS := optional -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) - LOCAL_REQUIRED_MODULES := file_contexts.bin -else - LOCAL_ADDITIONAL_DEPENDENCIES := file_contexts.bin -endif +LOCAL_REQUIRED_MODULES := file_contexts.bin + LOCAL_POST_INSTALL_CMD := \ $(hide) cp -f $(PRODUCT_OUT)/obj/ETC/file_contexts.bin_intermediates/file_contexts.concat.tmp $(TARGET_RECOVERY_ROOT_OUT)/file_contexts include $(BUILD_PHONY_PACKAGE) -ifneq ($(TW_USE_TOOLBOX), true) -include $(CLEAR_VARS) -# Create busybox symlinks... gzip and gunzip are excluded because those need to link to pigz instead -BUSYBOX_LINKS := $(shell cat external/busybox/busybox-full.links) -exclude := tune2fs mke2fs mkdosfs mkfs.vfat gzip gunzip - -# Having /sbin/modprobe present on 32 bit devices with can cause a massive -# performance problem if the kernel has CONFIG_MODULES=y -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0) - ifneq ($(TARGET_ARCH), arm64) - ifneq ($(TARGET_ARCH), x86_64) - exclude += modprobe - endif - endif -endif - -# If busybox does not have restorecon, assume it does not have SELinux support. -# Then, let toolbox provide 'ls' so -Z is available to list SELinux contexts. -ifeq ($(filter restorecon, $(notdir $(BUSYBOX_LINKS))),) - exclude += ls -endif - -RECOVERY_BUSYBOX_TOOLS := $(filter-out $(exclude), $(notdir $(BUSYBOX_LINKS))) -RECOVERY_BUSYBOX_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/, $(RECOVERY_BUSYBOX_TOOLS)) -$(RECOVERY_BUSYBOX_SYMLINKS): BUSYBOX_BINARY := busybox -$(RECOVERY_BUSYBOX_SYMLINKS): $(LOCAL_INSTALLED_MODULE) - @echo "Symlink: $@ -> $(BUSYBOX_BINARY)" - @mkdir -p $(dir $@) - @rm -rf $@ - $(hide) ln -sf $(BUSYBOX_BINARY) $@ - -include $(CLEAR_VARS) -LOCAL_MODULE := busybox_symlinks -LOCAL_MODULE_TAGS := optional -LOCAL_ADDITIONAL_DEPENDENCIES := $(RECOVERY_BUSYBOX_SYMLINKS) -ifneq (,$(filter $(PLATFORM_SDK_VERSION),16 17 18)) -ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_BUSYBOX_SYMLINKS) -endif -include $(BUILD_PHONY_PACKAGE) -RECOVERY_BUSYBOX_SYMLINKS := -endif # !TW_USE_TOOLBOX - # recovery-persist (system partition dynamic executable run after /data mounts) # =============================== -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0) - include $(CLEAR_VARS) - LOCAL_SRC_FILES := \ - recovery-persist.cpp \ - rotate_logs.cpp - LOCAL_MODULE := recovery-persist - LOCAL_SHARED_LIBRARIES := liblog libbase - LOCAL_CFLAGS := -Werror - LOCAL_INIT_RC := recovery-persist.rc - include $(BUILD_EXECUTABLE) -endif +include $(CLEAR_VARS) +LOCAL_SRC_FILES := \ + recovery-persist.cpp +LOCAL_MODULE := recovery-persist +LOCAL_SHARED_LIBRARIES := liblog libbase libmetricslogger +LOCAL_STATIC_LIBRARIES := libotautil +LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include +LOCAL_C_INCLUDES += system/core/libmetricslogger/include \ + system/core/libstats/include +LOCAL_CFLAGS := -Werror +LOCAL_INIT_RC := recovery-persist.rc +include $(BUILD_EXECUTABLE) # recovery-refresh (system partition dynamic executable run at init) # =============================== -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0) - include $(CLEAR_VARS) - LOCAL_SRC_FILES := \ - recovery-refresh.cpp \ - rotate_logs.cpp - LOCAL_MODULE := recovery-refresh - LOCAL_SHARED_LIBRARIES := liblog libbase - LOCAL_CFLAGS := -Werror - LOCAL_INIT_RC := recovery-refresh.rc - include $(BUILD_EXECUTABLE) -endif - -# shared libfusesideload -# =============================== -include $(CLEAR_VARS) -LOCAL_CLANG := true -LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE - -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := libfusesideload -LOCAL_SHARED_LIBRARIES := libcutils libc -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0) - LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes - LOCAL_SHARED_LIBRARIES += libmincrypttwrp - LOCAL_CFLAGS += -DUSE_MINCRYPT -else - LOCAL_SHARED_LIBRARIES += libcrypto -endif -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) - LOCAL_SRC_FILES := fuse_sideload22.cpp - LOCAL_CFLAGS += -DUSE_FUSE_SIDELOAD22 -else - LOCAL_SRC_FILES := fuse_sideload.cpp -endif -include $(BUILD_SHARED_LIBRARY) - -# static libfusesideload -# =============================== (required to fix build errors in 8.1 due to use by tests) include $(CLEAR_VARS) -LOCAL_CLANG := true -LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE - -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := libfusesideload -LOCAL_SHARED_LIBRARIES := libcutils libc -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0) - LOCAL_C_INCLUDES := $(LOCAL_PATH)/libmincrypt/includes - LOCAL_STATIC_LIBRARIES += libmincrypttwrp - LOCAL_CFLAGS += -DUSE_MINCRYPT -else - LOCAL_STATIC_LIBRARIES += libcrypto_static -endif -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) - LOCAL_SRC_FILES := fuse_sideload22.cpp - LOCAL_CFLAGS += -DUSE_FUSE_SIDELOAD22 -else - LOCAL_SRC_FILES := fuse_sideload.cpp -endif -include $(BUILD_STATIC_LIBRARY) +LOCAL_SRC_FILES := \ + recovery-refresh.cpp +LOCAL_MODULE := recovery-refresh +LOCAL_SHARED_LIBRARIES := liblog libbase +LOCAL_STATIC_LIBRARIES := libotautil +LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include +LOCAL_CFLAGS := -Werror +LOCAL_INIT_RC := recovery-refresh.rc +include $(BUILD_EXECUTABLE) # libmounts (static library) # =============================== @@ -708,7 +590,7 @@ LOCAL_MODULE := librecovery LOCAL_STATIC_LIBRARIES := \ libminui \ libotautil \ - libvintf_recovery \ + libvintf \ libcrypto_utils \ libcrypto \ libbase \ @@ -722,128 +604,43 @@ include $(CLEAR_VARS) LOCAL_MODULE := libaosprecovery -LOCAL_MODULE_TAGS := eng optional -LOCAL_CFLAGS := -std=gnu++0x -LOCAL_SRC_FILES := adb_install.cpp legacy_property_service.cpp set_metadata.cpp tw_atomic.cpp installcommand.cpp zipwrap.cpp -LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils libfusesideload libselinux libminzip +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := install/adb_install.cpp install/asn1_decoder.cpp install/fuse_sdcard_install.cpp \ + install/get_args.cpp install/install.cpp install/legacy_property_service.cpp \ + install/package.cpp install/verifier.cpp install/wipe_data.cpp \ + install/set_metadata.cpp install/zipwrap.cpp install/ZipUtil.cpp +LOCAL_SHARED_LIBRARIES += libbase libbootloader_message libcrypto libext4_utils \ + libfs_mgr libfusesideload libhidl-gen-utils libhidlbase \ + liblog libselinux libtinyxml2 libutils libz libziparchive libcutils +LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) +LOCAL_SHARED_LIBRARIES += libc++ +LOCAL_CFLAGS := -std=gnu++2a +LOCAL_C_INCLUDES += $(commands_TWRP_local_path)/install/include \ + $(commands_TWRP_local_path)/recovery_ui/include \ + $(commands_TWRP_local_path)/otautil/include \ + $(commands_TWRP_local_path)/minadbd \ + $(commands_TWRP_local_path)/minzip \ + $(commands_TWRP_local_path)/twrpinstall/include \ + system/libvintf/include +LOCAL_STATIC_LIBRARIES += libotautil libvintf_recovery libvintf libhidl-gen-utils LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) - LOCAL_SHARED_LIBRARIES += libstdc++ libstlport - LOCAL_C_INCLUDES += bionic external/stlport/stlport - LOCAL_CFLAGS += -DUSE_FUSE_SIDELOAD22 -else - LOCAL_SHARED_LIBRARIES += libc++ -endif -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0) - LOCAL_SHARED_LIBRARIES += libmincrypttwrp - LOCAL_C_INCLUDES += $(LOCAL_PATH)/libmincrypt/includes - LOCAL_SRC_FILES += verifier24/verifier.cpp verifier24/asn1_decoder.cpp - LOCAL_CFLAGS += -DUSE_OLD_VERIFIER -else - LOCAL_SHARED_LIBRARIES += libcrypto libbase - LOCAL_SRC_FILES += verifier.cpp asn1_decoder.cpp - LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include -endif ifeq ($(AB_OTA_UPDATER),true) LOCAL_CFLAGS += -DAB_OTA_UPDATER=1 endif -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) - LOCAL_SRC_FILES += otautil/ZipUtil.cpp otautil/SysUtil.cpp otautil/DirUtil.cpp - LOCAL_SHARED_LIBRARIES += libziparchive libext4_utils libcrypto libcrypto_utils - LOCAL_STATIC_LIBRARIES += libvintf_recovery libfs_mgr liblogwrap libavb libvintf libtinyxml2 libz - LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include - ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 27; echo $$?),0) - # Android 9.0 needs c++17 for libvintf - LOCAL_CPPFLAGS += -std=c++17 - # Android 9.0's libvintf also needs this library - LOCAL_STATIC_LIBRARIES += libhidl-gen-utils - endif -else - LOCAL_CFLAGS += -DUSE_MINZIP -endif include $(BUILD_SHARED_LIBRARY) -# libverifier (static library) -# =============================== -include $(CLEAR_VARS) -LOCAL_CLANG := true -LOCAL_MODULE := libverifier -LOCAL_SRC_FILES := \ - asn1_decoder.cpp \ - verifier.cpp -LOCAL_STATIC_LIBRARIES := \ - libotautil \ - libcrypto_utils \ - libcrypto \ - libbase -LOCAL_CFLAGS := -Wall -Werror -include $(BUILD_STATIC_LIBRARY) - -# Wear default device -# =============================== -include $(CLEAR_VARS) -LOCAL_SRC_FILES := wear_device.cpp -LOCAL_CFLAGS := -Wall -Werror - -# Should match TARGET_RECOVERY_UI_LIB in BoardConfig.mk. -LOCAL_MODULE := librecovery_ui_wear - -include $(BUILD_STATIC_LIBRARY) - -# vr headset default device -# =============================== -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := vr_device.cpp -LOCAL_CFLAGS := -Wall -Werror - -# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk -LOCAL_MODULE := librecovery_ui_vr - -include $(BUILD_STATIC_LIBRARY) commands_recovery_local_path := $(LOCAL_PATH) -# $(LOCAL_PATH)/edify/Android.mk -# $(LOCAL_PATH)/otafault/Android.mk -# $(LOCAL_PATH)/bootloader_message/Android.mk include \ - $(commands_TWRP_local_path)/boot_control/Android.mk \ - $(commands_TWRP_local_path)/tests/Android.mk \ - $(commands_TWRP_local_path)/tools/Android.mk \ - $(commands_TWRP_local_path)/updater/Android.mk \ - $(commands_TWRP_local_path)/update_verifier/Android.mk \ - $(commands_TWRP_local_path)/bootloader_message_twrp/Android.mk - -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -le 25; echo $$?),0) -include $(commands_TWRP_local_path)/bootloader_message/Android.mk -endif - -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) - include $(commands_TWRP_local_path)/mtp/ffs/Android.mk -else - include $(commands_TWRP_local_path)/mtp/legacy/Android.mk -endif + $(commands_TWRP_local_path)/updater/Android.mk -ifeq ($(wildcard system/core/uncrypt/Android.mk),) - #include $(commands_TWRP_local_path)/uncrypt/Android.mk -endif - -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 22; echo $$?),0) - ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0) - TARGET_GLOBAL_CFLAGS += -DTW_USE_MINUI_WITH_DATA - CLANG_TARGET_GLOBAL_CFLAGS += -DTW_USE_MINUI_WITH_DATA - endif - include $(commands_TWRP_local_path)/minadbd/Android.mk \ - $(commands_TWRP_local_path)/minui/Android.mk -else - TARGET_GLOBAL_CFLAGS += -DTW_USE_MINUI_21 - include $(commands_TWRP_local_path)/minadbd21/Android.mk \ - $(commands_TWRP_local_path)/minui21/Android.mk -endif +include $(commands_TWRP_local_path)/mtp/ffs/Android.mk \ + $(commands_TWRP_local_path)/minadbd/Android.mk \ + $(commands_TWRP_local_path)/minui/Android.mk -#$(commands_TWRP_local_path)/otautil/Android.mk #includes for TWRP include $(commands_TWRP_local_path)/injecttwrp/Android.mk \ $(commands_TWRP_local_path)/htcdumlock/Android.mk \ @@ -859,27 +656,26 @@ include $(commands_TWRP_local_path)/injecttwrp/Android.mk \ $(commands_TWRP_local_path)/libblkid/Android.mk \ $(commands_TWRP_local_path)/minuitwrp/Android.mk \ $(commands_TWRP_local_path)/openaes/Android.mk \ - $(commands_TWRP_local_path)/toolbox/Android.mk \ $(commands_TWRP_local_path)/twrpTarMain/Android.mk \ $(commands_TWRP_local_path)/minzip/Android.mk \ $(commands_TWRP_local_path)/dosfstools/Android.mk \ $(commands_TWRP_local_path)/etc/Android.mk \ - $(commands_TWRP_local_path)/toybox/Android.mk \ $(commands_TWRP_local_path)/simg2img/Android.mk \ $(commands_TWRP_local_path)/adbbu/Android.mk \ $(commands_TWRP_local_path)/libpixelflinger/Android.mk \ $(commands_TWRP_local_path)/twrpDigest/Android.mk \ $(commands_TWRP_local_path)/attr/Android.mk -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 24; echo $$?),0) - include $(commands_TWRP_local_path)/libmincrypt/Android.mk +ifneq ($(TW_OZIP_DECRYPT_KEY),) + TWRP_REQUIRED_MODULES += ozip_decrypt + include $(commands_TWRP_local_path)/ozip_decrypt/Android.mk endif 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 + include $(commands_TWRP_local_path)/crypto/fscrypt/Android.mk endif ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),) ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false) diff --git a/CleanSpec.mk b/CleanSpec.mk index e2d97d42ba..a7ab0d9bec 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -44,8 +44,13 @@ #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libminui_intermediates/import_includes) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinstall.recovery_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/system/lib64/libinstall.so) + # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libminui_intermediates/import_includes) diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index b5f5f03628..28aa06f455 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -4,3 +4,7 @@ clang_format = true [Builtin Hooks Options] # Handle native codes only. clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp + +[Hook Scripts] +checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} + --file_whitelist tools/ updater_sample/ diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/adb_install.cpp b/adb_install.cpp deleted file mode 100644 index 291708c69c..0000000000 --- a/adb_install.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * 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. - */ - -#include "adb_install.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ui.h" -#include "cutils/properties.h" - -#include "common.h" -#include "fuse_sideload.h" -#ifdef USE_OLD_VERIFIER -#include "verifier24/verifier.h" -#else -#include "verifier.h" -#endif - -static void set_usb_driver(bool enabled) { - char configfs[PROPERTY_VALUE_MAX]; - property_get("sys.usb.configfs", configfs, "false"); - if (strcmp(configfs, "false") == 0 || strcmp(configfs, "0") == 0) - return; - - int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY); - if (fd < 0) { -/* These error messages show when built in older Android branches (e.g. Gingerbread) - It's not a critical error so we're disabling the error messages. - ui->Print("failed to open driver control: %s\n", strerror(errno)); -*/ - printf("failed to open driver control: %s\n", strerror(errno)); - return; - } - - if (TEMP_FAILURE_RETRY(write(fd, enabled ? "1" : "0", 1)) == -1) { -/* - ui->Print("failed to set driver control: %s\n", strerror(errno)); -*/ - printf("failed to set driver control: %s\n", strerror(errno)); - } - if (close(fd) < 0) { -/* - ui->Print("failed to close driver control: %s\n", strerror(errno)); -*/ - printf("failed to close driver control: %s\n", strerror(errno)); - } -} - -// On Android 8.0 for some reason init can't seem to completely stop adbd -// so we have to kill it too if it doesn't die on its own. -static void kill_adbd() { - 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, "adbd") != 0) { - printf("adbd pid %d found, sending kill.\n", pid); - kill(pid, SIGINT); - usleep(5000); - kill(pid, SIGKILL); - } - } - fclose(file); - } - } - closedir(dir); - } -} - -static void stop_adbd() { - printf("Stopping adbd...\n"); - property_set("ctl.stop", "adbd"); - usleep(5000); - kill_adbd(); - set_usb_driver(false); -} - -static bool is_ro_debuggable() { - char value[PROPERTY_VALUE_MAX+1]; - return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1'); -} - -static void maybe_restart_adbd() { - if (is_ro_debuggable()) { - printf("Restarting adbd...\n"); - set_usb_driver(true); - property_set("ctl.start", "adbd"); - } -} - -// How long (in seconds) we wait for the host to start sending us a -// package, before timing out. -#define ADB_INSTALL_TIMEOUT 300 - -int -apply_from_adb(const char* install_file, pid_t* child_pid) { - - stop_adbd(); - set_usb_driver(true); -/* -int apply_from_adb(RecoveryUI* ui, bool* wipe_cache, const char* install_file) { - modified_flash = true; - - stop_adbd(ui); - set_usb_driver(ui, true); - - ui->Print("\n\nNow send the package you want to apply\n" - "to the device with \"adb sideload \"...\n"); -*/ - pid_t child; - if ((child = fork()) == 0) { - execl("/sbin/recovery", "recovery", "--adbd", install_file, NULL); - _exit(-1); - } - - *child_pid = child; - // caller can now kill the child thread from another thread - - // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host - // connects and starts serving a package. Poll for its - // appearance. (Note that inotify doesn't work with FUSE.) - int result = INSTALL_ERROR; - int status; - bool waited = false; - struct stat st; - for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { - if (waitpid(child, &status, WNOHANG) != 0) { - result = -1; - waited = true; - break; - } - - if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { - if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) { - sleep(1); - continue; - } else { - printf("\nTimed out waiting for package: %s\n\n", strerror(errno)); - result = -1; - kill(child, SIGKILL); - break; - } - } - // Install is handled elsewhere in TWRP - //install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false); - return 0; - } - - // if we got here, something failed - *child_pid = 0; - - if (!waited) { - // Calling stat() on this magic filename signals the minadbd - // subprocess to shut down. - stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); - - // TODO(dougz): there should be a way to cancel waiting for a - // package (by pushing some button combo on the device). For now - // you just have to 'adb sideload' a file that's not a valid - // package, like "/dev/null". - waitpid(child, &status, 0); - } - - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - if (WEXITSTATUS(status) == 3) { - printf("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n"); - result = -2; - } else if (!WIFSIGNALED(status)) { - printf("adbd status %d\n", WEXITSTATUS(status)); - } - } - - set_usb_driver(false); - maybe_restart_adbd(); - - return result; -} diff --git a/adbbu/Android.mk b/adbbu/Android.mk old mode 100644 new mode 100755 index 8f8dbd0fb1..c05f265d5d --- a/adbbu/Android.mk +++ b/adbbu/Android.mk @@ -41,8 +41,7 @@ LOCAL_C_INCLUDES += bionic external/zlib LOCAL_CFLAGS:= -c -W LOCAL_MODULE:= twrpbu LOCAL_MODULE_STEM := bu -LOCAL_MODULE_TAGS:= eng +LOCAL_MODULE_TAGS:= optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin include $(BUILD_EXECUTABLE) - diff --git a/applypatch/Android.bp b/applypatch/Android.bp index cb0b367461..620ca6cc98 100644 --- a/applypatch/Android.bp +++ b/applypatch/Android.bp @@ -53,7 +53,6 @@ cc_library_static { "libbz", "libcrypto", "libedify", - "libotafault", "libotautil", "libz", ], @@ -100,7 +99,6 @@ cc_binary { "libapplypatch_modes", "libapplypatch", "libedify", - "libotafault", "libotautil", "libbspatch", ], diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 1f71c44652..44112fde47 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -23,7 +23,7 @@ LOCAL_SRC_FILES := \ freecache.cpp \ imgpatch.cpp LOCAL_MODULE := libapplypatch -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include \ $(commands_recovery_local_path) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp old mode 100644 new mode 100755 index 5c6c83f4f7..0106582f6d --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -23,749 +23,395 @@ #include #include #include -#include #include #include +#include #include #include #include #include #include +#include #include #include #include +#include #include #include "bmlutils/bmlutils.h" #include "mtdutils/mtdutils.h" #include "edify/expr.h" -#include "otafault/ota_io.h" -#include "otautil/cache_location.h" +#include "otautil/paths.h" #include "otautil/print_sha1.h" -static int LoadPartitionContents(const std::string& filename, FileContents* file); -static size_t FileSink(const unsigned char* data, size_t len, int fd); -static int GenerateTarget(const FileContents& source_file, const std::unique_ptr& patch, - const std::string& target_filename, - const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data); - -static bool mtd_partitions_scanned = false; - -// Read a file into memory; store the file contents and associated metadata in *file. -// Return 0 on success. -int LoadFileContents(const char* filename, FileContents* file) { - // A special 'filename' beginning with "MTD:" or "EMMC:" means to - // load the contents of a partition. - if (strncmp(filename, "MTD:", 4) == 0 || - strncmp(filename, "EMMC:", 5) == 0 || - strncmp(filename, "BML:", 4) == 0) { - return LoadPartitionContents(filename, file); - } +using namespace std::string_literals; - struct stat sb; - if (stat(filename, &sb) == -1) { - printf("failed to stat \"%s\": %s\n", filename, strerror(errno)); - return -1; - } +static bool GenerateTarget(const Partition& target, const FileContents& source_file, + const Value& patch, const Value* bonus_data); - std::vector data(sb.st_size); - unique_file f(ota_fopen(filename, "rb")); - if (!f) { - printf("failed to open \"%s\": %s\n", filename, strerror(errno)); - return -1; +bool LoadFileContents(const std::string& filename, FileContents* file) { + // No longer allow loading contents from eMMC partitions. + if (android::base::StartsWith(filename, "EMMC:")) { + return false; } - size_t bytes_read = ota_fread(data.data(), 1, data.size(), f.get()); - if (bytes_read != data.size()) { - printf("short read of \"%s\" (%zu bytes of %zu)\n", filename, bytes_read, data.size()); - return -1; + std::string data; + if (!android::base::ReadFileToString(filename, &data)) { + PLOG(ERROR) << "Failed to read \"" << filename << "\""; + return false; } - file->data = std::move(data); + + file->data = std::vector(data.begin(), data.end()); SHA1(file->data.data(), file->data.size(), file->sha1); - return 0; + return true; } -// Load the contents of an EMMC partition into the provided -// FileContents. filename should be a string of the form -// "EMMC::...". The smallest size_n bytes for -// which that prefix of the partition contents has the corresponding -// sha1 hash will be loaded. It is acceptable for a size value to be -// repeated with different sha1s. Will return 0 on success. -// -// This complexity is needed because if an OTA installation is -// interrupted, the partition might contain either the source or the -// target data, which might be of different lengths. We need to know -// the length in order to read from a partition (there is no -// "end-of-file" marker), so the caller must specify the possible -// lengths and the hash of the data, and we'll do the load expecting -// to find one of those hashes. -enum PartitionType { MTD, EMMC }; - -static int LoadPartitionContents(const std::string& filename, FileContents* file) { - std::vector pieces = android::base::Split(filename, ":"); - if (pieces.size() < 4 || pieces.size() % 2 != 0) { - printf("LoadPartitionContents called with bad filename \"%s\"\n", filename.c_str()); - return -1; +// Reads the contents of a Partition to the given FileContents buffer. +static bool ReadPartitionToBuffer(const Partition& partition, FileContents* out, + bool check_backup) { + uint8_t expected_sha1[SHA_DIGEST_LENGTH]; + if (ParseSha1(partition.hash, expected_sha1) != 0) { + LOG(ERROR) << "Failed to parse target hash \"" << partition.hash << "\""; + return false; } - enum PartitionType type; - if (pieces[0] == "MTD") { - type = MTD; - } else if (pieces[0] == "EMMC") { - type = EMMC; - } else if (pieces[0] == "BML") { - type = EMMC; + android::base::unique_fd dev(open(partition.name.c_str(), O_RDONLY)); + if (dev == -1) { + PLOG(ERROR) << "Failed to open eMMC partition \"" << partition << "\""; } else { - printf("LoadPartitionContents called with bad filename (%s)\n", filename.c_str()); - return -1; - } - - size_t pair_count = (pieces.size() - 2) / 2; // # of (size, sha1) pairs in filename - std::vector> pairs; - for (size_t i = 0; i < pair_count; ++i) { - size_t size; - if (!android::base::ParseUint(pieces[i * 2 + 2], &size) || size == 0) { - printf("LoadPartitionContents called with bad size \"%s\"\n", pieces[i * 2 + 2].c_str()); - return -1; - } - pairs.push_back({ size, pieces[i * 2 + 3] }); - } - - // Sort the pairs array so that they are in order of increasing size. - std::sort(pairs.begin(), pairs.end()); - - const char* partition = pieces[1].c_str(); - unique_file dev(ota_fopen(partition, "rb")); - if (!dev) { - printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno)); - return -1; - } - - SHA_CTX sha_ctx; - SHA1_Init(&sha_ctx); - - // Allocate enough memory to hold the largest size. - std::vector buffer(pairs[pair_count - 1].first); - unsigned char* buffer_ptr = buffer.data(); - size_t buffer_size = 0; // # bytes read so far - bool found = false; - - for (const auto& pair : pairs) { - size_t current_size = pair.first; - const std::string& current_sha1 = pair.second; - - // Read enough additional bytes to get us up to the next size. (Again, - // we're trying the possibilities in order of increasing size). - size_t next = current_size - buffer_size; - if (next > 0) { - size_t read = ota_fread(buffer_ptr, 1, next, dev.get()); - if (next != read) { - printf("short read (%zu bytes of %zu) for partition \"%s\"\n", read, next, partition); - return -1; - } - SHA1_Update(&sha_ctx, buffer_ptr, read); - buffer_size += read; - buffer_ptr += read; - } - - if (pieces[0] == "BML") { - if (strcmp(partition, "boot") == 0) { - partition = BOARD_BML_BOOT; - } else if (strcmp(partition, "recovery") == 0) { - partition = BOARD_BML_RECOVERY; + std::vector buffer(partition.size); + if (!android::base::ReadFully(dev, buffer.data(), buffer.size())) { + PLOG(ERROR) << "Failed to read " << buffer.size() << " bytes of data for partition " + << partition; + } else { + SHA1(buffer.data(), buffer.size(), out->sha1); + if (memcmp(out->sha1, expected_sha1, SHA_DIGEST_LENGTH) == 0) { + out->data = std::move(buffer); + return true; } } - - // Duplicate the SHA context and finalize the duplicate so we can - // check it against this pair's expected hash. - SHA_CTX temp_ctx; - memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX)); - uint8_t sha_so_far[SHA_DIGEST_LENGTH]; - SHA1_Final(sha_so_far, &temp_ctx); - - uint8_t parsed_sha[SHA_DIGEST_LENGTH]; - if (ParseSha1(current_sha1.c_str(), parsed_sha) != 0) { - printf("failed to parse SHA-1 %s in %s\n", current_sha1.c_str(), filename.c_str()); - return -1; - } - - if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_LENGTH) == 0) { - // We have a match. Stop reading the partition; we'll return the data we've read so far. - printf("partition read matched size %zu SHA-1 %s\n", current_size, current_sha1.c_str()); - found = true; - break; - } } - if (!found) { - // Ran off the end of the list of (size, sha1) pairs without finding a match. - printf("contents of partition \"%s\" didn't match %s\n", partition, filename.c_str()); - return -1; + if (!check_backup) { + LOG(ERROR) << "Partition contents don't have the expected checksum"; + return false; } - SHA1_Final(file->sha1, &sha_ctx); - - buffer.resize(buffer_size); - file->data = std::move(buffer); + if (LoadFileContents(Paths::Get().cache_temp_source(), out) && + memcmp(out->sha1, expected_sha1, SHA_DIGEST_LENGTH) == 0) { + return true; + } - return 0; + LOG(ERROR) << "Both of partition contents and backup don't have the expected checksum"; + return false; } -// Save the contents of the given FileContents object under the given -// filename. Return 0 on success. -int SaveFileContents(const char* filename, const FileContents* file) { - unique_fd fd(ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR)); +bool SaveFileContents(const std::string& filename, const FileContents* file) { + android::base::unique_fd fd( + open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR)); if (fd == -1) { - printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno)); - return -1; + PLOG(ERROR) << "Failed to open \"" << filename << "\" for write"; + return false; } - size_t bytes_written = FileSink(file->data.data(), file->data.size(), fd); - if (bytes_written != file->data.size()) { - printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written, - file->data.size(), strerror(errno)); - return -1; + if (!android::base::WriteFully(fd, file->data.data(), file->data.size())) { + PLOG(ERROR) << "Failed to write " << file->data.size() << " bytes of data to " << filename; + return false; } - if (ota_fsync(fd) != 0) { - printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno)); - return -1; + + if (fsync(fd) != 0) { + PLOG(ERROR) << "Failed to fsync \"" << filename << "\""; + return false; } - if (ota_close(fd) != 0) { - printf("close of \"%s\" failed: %s\n", filename, strerror(errno)); - return -1; + + if (close(fd.release()) != 0) { + PLOG(ERROR) << "Failed to close \"" << filename << "\""; + return false; } - return 0; + return true; } -// Write a memory buffer to 'target' partition, a string of the form -// "EMMC:[:...]". The target name -// might contain multiple colons, but WriteToPartition() only uses the first -// two and ignores the rest. Return 0 on success. -int WriteToPartition(const unsigned char* data, size_t len, const std::string& target) { - std::string copy(target); - std::vector pieces = android::base::Split(copy, ":"); - - if (pieces.size() < 2) { - printf("WriteToPartition called with bad target (%s)\n", target.c_str()); - return -1; +// Writes a memory buffer to 'target' Partition. +static bool WriteBufferToPartition(const FileContents& file_contents, const Partition& partition) { + const unsigned char* data = file_contents.data.data(); + size_t len = file_contents.data.size(); + size_t start = 0; + bool success = false; + for (size_t attempt = 0; attempt < 2; ++attempt) { + android::base::unique_fd fd(open(partition.name.c_str(), O_RDWR)); + if (fd == -1) { + PLOG(ERROR) << "Failed to open \"" << partition << "\""; + return false; } - enum PartitionType type; - if (pieces[0] == "MTD") { - type = MTD; - } else if (pieces[0] == "EMMC") { - type = EMMC; - } else if (pieces[0] == "BML") { - type = EMMC; - } else { - printf("WriteToPartition called with bad target (%s)\n", target.c_str()); - return -1; + if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) { + PLOG(ERROR) << "Failed to seek to " << start << " on \"" << partition << "\""; + return false; } - const char* partition = pieces[1].c_str(); - - if (pieces[0] == "BML") { - if (strcmp(partition, "boot") == 0) { - partition = BOARD_BML_BOOT; - } else if (strcmp(partition, "recovery") == 0) { - partition = BOARD_BML_RECOVERY; - } - - int bmlpartition = open(partition, O_RDWR | O_LARGEFILE); - if (bmlpartition < 0) - return -1; - if (ioctl(bmlpartition, BML_UNLOCK_ALL, 0)) { - printf("failed to unlock BML partition: (%s)\n", partition); - return -1; - } - close(bmlpartition); + if (!android::base::WriteFully(fd, data + start, len - start)) { + PLOG(ERROR) << "Failed to write " << len - start << " bytes to \"" << partition << "\""; + return false; } - if (partition == NULL) { - printf("bad partition target name \"%s\"\n", target.c_str()); - return -1; + if (fsync(fd) != 0) { + PLOG(ERROR) << "Failed to sync \"" << partition << "\""; + return false; } - - switch (type) { - case MTD: { - if (!mtd_partitions_scanned) { - mtd_scan_partitions(); - mtd_partitions_scanned = true; - } - - const MtdPartition* mtd = mtd_find_partition_by_name(partition); - if (mtd == NULL) { - printf("mtd partition \"%s\" not found for writing\n", partition); - return -1; - } - - MtdWriteContext* ctx = mtd_write_partition(mtd); - if (ctx == NULL) { - printf("failed to init mtd partition \"%s\" for writing\n", partition); - return -1; - } - - size_t written = mtd_write_data(ctx, reinterpret_cast(data), len); - if (written != len) { - printf("only wrote %zu of %zu bytes to MTD %s\n", written, len, partition); - mtd_write_close(ctx); - return -1; - } - - if (mtd_erase_blocks(ctx, -1) < 0) { - printf("error finishing mtd write of %s\n", partition); - mtd_write_close(ctx); - return -1; - } - - if (mtd_write_close(ctx)) { - printf("error closing mtd write of %s\n", partition); - return -1; - } - break; - } - - case EMMC: { - size_t start = 0; - bool success = false; - unique_fd fd(ota_open(partition, O_RDWR | O_SYNC)); - if (fd < 0) { - printf("failed to open %s: %s\n", partition, strerror(errno)); - return -1; - } - - for (size_t attempt = 0; attempt < 2; ++attempt) { - if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) { - printf("failed seek on %s: %s\n", partition, strerror(errno)); - return -1; - } - while (start < len) { - size_t to_write = len - start; - if (to_write > 1<<20) to_write = 1<<20; - - ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data+start, to_write)); - if (written == -1) { - printf("failed write writing to %s: %s\n", partition, strerror(errno)); - return -1; - } - start += written; - } - if (ota_fsync(fd) != 0) { - printf("failed to sync to %s (%s)\n", partition, strerror(errno)); - return -1; - } - if (ota_close(fd) != 0) { - printf("failed to close %s (%s)\n", partition, strerror(errno)); - return -1; - } - unique_fd fd(ota_open(partition, O_RDONLY)); - if (fd < 0) { - printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno)); - return -1; - } - - // Drop caches so our subsequent verification read - // won't just be reading the cache. - sync(); - unique_fd dc(ota_open("/proc/sys/vm/drop_caches", O_WRONLY)); - if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) { - printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno)); - } else { - printf(" caches dropped\n"); - } - ota_close(dc); - sleep(1); - - // verify - if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) { - printf("failed to seek back to beginning of %s: %s\n", - partition, strerror(errno)); - return -1; - } - unsigned char buffer[4096]; - start = len; - for (size_t p = 0; p < len; p += sizeof(buffer)) { - size_t to_read = len - p; - if (to_read > sizeof(buffer)) { - to_read = sizeof(buffer); - } - - size_t so_far = 0; - while (so_far < to_read) { - ssize_t read_count = - TEMP_FAILURE_RETRY(ota_read(fd, buffer+so_far, to_read-so_far)); - if (read_count == -1) { - printf("verify read error %s at %zu: %s\n", - partition, p, strerror(errno)); - return -1; - } - if (static_cast(read_count) < to_read) { - printf("short verify read %s at %zu: %zd %zu %s\n", - partition, p, read_count, to_read, strerror(errno)); - } - so_far += read_count; - } - - if (memcmp(buffer, data+p, to_read) != 0) { - printf("verification failed starting at %zu\n", p); - start = p; - break; - } - } - - if (start == len) { - printf("verification read succeeded (attempt %zu)\n", attempt+1); - success = true; - break; - } - } - - if (!success) { - printf("failed to verify after all attempts\n"); - return -1; - } - - if (ota_close(fd) != 0) { - printf("error closing %s (%s)\n", partition, strerror(errno)); - return -1; - } - sync(); - break; - } + if (close(fd.release()) != 0) { + PLOG(ERROR) << "Failed to close \"" << partition << "\""; + return false; } - return 0; -} - -// Take a string 'str' of 40 hex digits and parse it into the 20 -// byte array 'digest'. 'str' may contain only the digest or be of -// the form ":". Return 0 on success, -1 on any -// error. -int ParseSha1(const char* str, uint8_t* digest) { - const char* ps = str; - uint8_t* pd = digest; - for (int i = 0; i < SHA_DIGEST_LENGTH * 2; ++i, ++ps) { - int digit; - if (*ps >= '0' && *ps <= '9') { - digit = *ps - '0'; - } else if (*ps >= 'a' && *ps <= 'f') { - digit = *ps - 'a' + 10; - } else if (*ps >= 'A' && *ps <= 'F') { - digit = *ps - 'A' + 10; - } else { - return -1; - } - if (i % 2 == 0) { - *pd = digit << 4; - } else { - *pd |= digit; - ++pd; - } + fd.reset(open(partition.name.c_str(), O_RDONLY)); + if (fd == -1) { + PLOG(ERROR) << "Failed to reopen \"" << partition << "\" for verification"; + return false; } - if (*ps != '\0') return -1; - return 0; -} -// Search an array of sha1 strings for one matching the given sha1. -// Return the index of the match on success, or -1 if no match is -// found. -static int FindMatchingPatch(uint8_t* sha1, const std::vector& patch_sha1_str) { - for (size_t i = 0; i < patch_sha1_str.size(); ++i) { - uint8_t patch_sha1[SHA_DIGEST_LENGTH]; - if (ParseSha1(patch_sha1_str[i].c_str(), patch_sha1) == 0 && - memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) { - return i; + // Drop caches so our subsequent verification read won't just be reading the cache. + sync(); + std::string drop_cache = "/proc/sys/vm/drop_caches"; + if (!android::base::WriteStringToFile("3\n", drop_cache)) { + PLOG(ERROR) << "Failed to write to " << drop_cache; + } else { + LOG(INFO) << " caches dropped"; } - } - return -1; -} + sleep(1); -// Returns 0 if the contents of the file (argv[2]) or the cached file -// match any of the sha1's on the command line (argv[3:]). Returns -// nonzero otherwise. -int applypatch_check(const char* filename, const std::vector& patch_sha1_str) { - FileContents file; - - // It's okay to specify no sha1s; the check will pass if the - // LoadFileContents is successful. (Useful for reading - // partitions, where the filename encodes the sha1s; no need to - // check them twice.) - if (LoadFileContents(filename, &file) != 0 || - (!patch_sha1_str.empty() && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) { - printf("file \"%s\" doesn't have any of expected sha1 sums; checking cache\n", filename); - - // If the source file is missing or corrupted, it might be because we were killed in the middle - // of patching it. A copy of it should have been made in cache_temp_source. If that file - // exists and matches the sha1 we're looking for, the check still passes. - if (LoadFileContents(CacheLocation::location().cache_temp_source().c_str(), &file) != 0) { - printf("failed to load cache file\n"); - return 1; + // Verify. + if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) { + PLOG(ERROR) << "Failed to seek to 0 on " << partition; + return false; } - if (FindMatchingPatch(file.sha1, patch_sha1_str) < 0) { - printf("cache bits don't match any sha1 for \"%s\"\n", filename); - return 1; - } - } - return 0; -} + const char* partition = pieces[1].c_str(); -int ShowLicenses() { - ShowBSDiffLicense(); - return 0; -} + PLOG(ERROR) << "Failed to verify-read " << partition << " at " << p; + return false; + } -static size_t FileSink(const unsigned char* data, size_t len, int fd) { - size_t done = 0; - while (done < len) { - ssize_t wrote = TEMP_FAILURE_RETRY(ota_write(fd, data + done, len - done)); - if (wrote == -1) { - printf("error writing %zd bytes: %s\n", (len - done), strerror(errno)); - return done; + if (memcmp(buffer, data + p, to_read) != 0) { + LOG(ERROR) << "Verification failed starting at " << p; + start = p; + break; + } } - done += wrote; - } - return done; -} -// Return the amount of free space (in bytes) on the filesystem -// containing filename. filename must exist. Return -1 on error. -size_t FreeSpaceForFile(const char* filename) { - struct statfs sf; - if (statfs(filename, &sf) != 0) { - printf("failed to statfs %s: %s\n", filename, strerror(errno)); - return -1; + if (start == len) { + LOG(INFO) << "Verification read succeeded (attempt " << attempt + 1 << ")"; + success = true; + break; } - return sf.f_bsize * sf.f_bavail; -} -int CacheSizeCheck(size_t bytes) { - if (MakeFreeSpaceOnCache(bytes) < 0) { - printf("unable to make %zu bytes available on /cache\n", bytes); - return 1; + if (close(fd.release()) != 0) { + PLOG(ERROR) << "Failed to close " << partition; + return false; } - return 0; -} - -// This function applies binary patches to EMMC target files in a way that is safe (the original -// file is not touched until we have the desired replacement for it) and idempotent (it's okay to -// run this program multiple times). -// -// - If the SHA-1 hash of is , does nothing and exits -// successfully. -// -// - Otherwise, if the SHA-1 hash of is one of the entries in , -// the corresponding patch from (which must be a VAL_BLOB) is applied to produce a -// new file (the type of patch is automatically detected from the blob data). If that new file -// has SHA-1 hash , moves it to replace , and exits -// successfully. Note that if and are not the same, -// is NOT deleted on success. may be the string "-" to mean -// "the same as ". -// -// - Otherwise, or if any error is encountered, exits with non-zero status. -// -// must refer to an EMMC partition to read the source data. See the comments for -// the LoadPartitionContents() function above for the format of such a filename. has -// become obsolete since we have dropped the support for patching non-EMMC targets (EMMC targets -// have the size embedded in the filename). -int applypatch(const char* source_filename, const char* target_filename, - const char* target_sha1_str, size_t /* target_size */, - const std::vector& patch_sha1_str, - const std::vector>& patch_data, const Value* bonus_data) { - printf("patch %s: ", source_filename); - - if (target_filename[0] == '-' && target_filename[1] == '\0') { - target_filename = source_filename; } - if (strncmp(target_filename, "EMMC:", 5) != 0) { - printf("Supporting patching EMMC targets only.\n"); - return 1; + if (!success) { + LOG(ERROR) << "Failed to verify after all attempts"; + return false; } - uint8_t target_sha1[SHA_DIGEST_LENGTH]; - if (ParseSha1(target_sha1_str, target_sha1) != 0) { - printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str); - return 1; - } + sync(); - // We try to load the target file into the source_file object. - FileContents source_file; - if (LoadFileContents(target_filename, &source_file) == 0) { - if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) { - // The early-exit case: the patch was already applied, this file has the desired hash, nothing - // for us to do. - printf("already %s\n", short_sha1(target_sha1).c_str()); - return 0; + return true; +} + +int ParseSha1(const std::string& str, uint8_t* digest) { + const char* ps = str.c_str(); + uint8_t* pd = digest; + for (int i = 0; i < SHA_DIGEST_LENGTH * 2; ++i, ++ps) { + int digit; + if (*ps >= '0' && *ps <= '9') { + digit = *ps - '0'; + } else if (*ps >= 'a' && *ps <= 'f') { + digit = *ps - 'a' + 10; + } else if (*ps >= 'A' && *ps <= 'F') { + digit = *ps - 'A' + 10; + } else { + return -1; + } + if (i % 2 == 0) { + *pd = digit << 4; + } else { + *pd |= digit; + ++pd; } } + if (*ps != '\0') return -1; + return 0; +} - if (source_file.data.empty() || - (target_filename != source_filename && strcmp(target_filename, source_filename) != 0)) { - // Need to load the source file: either we failed to load the target file, or we did but it's - // different from the expected. - source_file.data.clear(); - LoadFileContents(source_filename, &source_file); - } +bool PatchPartitionCheck(const Partition& target, const Partition& source) { + FileContents target_file; + FileContents source_file; + return (ReadPartitionToBuffer(target, &target_file, false) || + ReadPartitionToBuffer(source, &source_file, true)); +} - if (!source_file.data.empty()) { - int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str); - if (to_use != -1) { - return GenerateTarget(source_file, patch_data[to_use], target_filename, target_sha1, - bonus_data); - } - } +int ShowLicenses() { + ShowBSDiffLicense(); + return 0; +} - printf("source file is bad; trying copy\n"); +bool PatchPartition(const Partition& target, const Partition& source, const Value& patch, + const Value* bonus) { + LOG(INFO) << "Patching " << target.name; - FileContents copy_file; - if (LoadFileContents(CacheLocation::location().cache_temp_source().c_str(), ©_file) < 0) { - printf("failed to read copy file\n"); - return 1; + // We try to load and check against the target hash first. + FileContents target_file; + if (ReadPartitionToBuffer(target, &target_file, false)) { + // The early-exit case: the patch was already applied, this file has the desired hash, nothing + // for us to do. + LOG(INFO) << " already " << target.hash.substr(0, 8); + return true; } - int to_use = FindMatchingPatch(copy_file.sha1, patch_sha1_str); - if (to_use == -1) { - printf("copy file doesn't match source SHA-1s either\n"); - return 1; + FileContents source_file; + if (ReadPartitionToBuffer(source, &source_file, true)) { + return GenerateTarget(target, source_file, patch, bonus); } - return GenerateTarget(copy_file, patch_data[to_use], target_filename, target_sha1, bonus_data); + LOG(ERROR) << "Failed to find any match"; + return false; } -/* - * This function flashes a given image to the target partition. It verifies - * the target cheksum first, and will return if target has the desired hash. - * It checks the checksum of the given source image before flashing, and - * verifies the target partition afterwards. The function is idempotent. - * Returns zero on success. - */ -int applypatch_flash(const char* source_filename, const char* target_filename, - const char* target_sha1_str, size_t target_size) { - printf("flash %s: ", target_filename); - - uint8_t target_sha1[SHA_DIGEST_LENGTH]; - if (ParseSha1(target_sha1_str, target_sha1) != 0) { - printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str); - return 1; - } +bool FlashPartition(const Partition& partition, const std::string& source_filename) { + LOG(INFO) << "Flashing " << partition; - std::string target_str(target_filename); - std::vector pieces = android::base::Split(target_str, ":"); - if (pieces.size() != 2 || pieces[0] != "EMMC") { - printf("invalid target name \"%s\"", target_filename); - return 1; + // We try to load and check against the target hash first. + FileContents target_file; + if (ReadPartitionToBuffer(partition, &target_file, false)) { + // The early-exit case: the patch was already applied, this file has the desired hash, nothing + // for us to do. + LOG(INFO) << " already " << partition.hash.substr(0, 8); + return true; } - // Load the target into the source_file object to see if already applied. - pieces.push_back(std::to_string(target_size)); - pieces.push_back(target_sha1_str); - std::string fullname = android::base::Join(pieces, ':'); FileContents source_file; - if (LoadPartitionContents(fullname, &source_file) == 0 && - memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) { - // The early-exit case: the image was already applied, this partition - // has the desired hash, nothing for us to do. - printf("already %s\n", short_sha1(target_sha1).c_str()); - return 0; + if (!LoadFileContents(source_filename, &source_file)) { + LOG(ERROR) << "Failed to load source file"; + return false; } - if (LoadFileContents(source_filename, &source_file) == 0) { - if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) { - // The source doesn't have desired checksum. - printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename); - printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(), - short_sha1(source_file.sha1).c_str()); - return 1; - } + uint8_t expected_sha1[SHA_DIGEST_LENGTH]; + if (ParseSha1(partition.hash, expected_sha1) != 0) { + LOG(ERROR) << "Failed to parse source hash \"" << partition.hash << "\""; + return false; } - if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) { - printf("write of copied data to %s failed\n", target_filename); - return 1; + if (memcmp(source_file.sha1, expected_sha1, SHA_DIGEST_LENGTH) != 0) { + // The source doesn't have desired checksum. + LOG(ERROR) << "source \"" << source_filename << "\" doesn't have expected SHA-1 sum"; + LOG(ERROR) << "expected: " << partition.hash.substr(0, 8) + << ", found: " << short_sha1(source_file.sha1); + return false; } - return 0; + if (!WriteBufferToPartition(source_file, partition)) { + LOG(ERROR) << "Failed to write to " << partition; + return false; + } + return true; } -static int GenerateTarget(const FileContents& source_file, const std::unique_ptr& patch, - const std::string& target_filename, - const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data) { - if (patch->type != VAL_BLOB) { - printf("patch is not a blob\n"); - return 1; +static bool GenerateTarget(const Partition& target, const FileContents& source_file, + const Value& patch, const Value* bonus_data) { + uint8_t expected_sha1[SHA_DIGEST_LENGTH]; + if (ParseSha1(target.hash, expected_sha1) != 0) { + LOG(ERROR) << "Failed to parse target hash \"" << target.hash << "\""; + return false; + } + + if (patch.type != Value::Type::BLOB) { + LOG(ERROR) << "patch is not a blob"; + return false; } - const char* header = &patch->data[0]; - size_t header_bytes_read = patch->data.size(); + const char* header = patch.data.data(); + size_t header_bytes_read = patch.data.size(); bool use_bsdiff = false; if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) { use_bsdiff = true; } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) { use_bsdiff = false; } else { - printf("Unknown patch file format\n"); - return 1; + LOG(ERROR) << "Unknown patch file format"; + return false; } - CHECK(android::base::StartsWith(target_filename, "EMMC:")); - - // We still write the original source to cache, in case the partition write is interrupted. - if (MakeFreeSpaceOnCache(source_file.data.size()) < 0) { - printf("not enough free space on /cache\n"); - return 1; + // We write the original source to cache, in case the partition write is interrupted. + if (!CheckAndFreeSpaceOnCache(source_file.data.size())) { + LOG(ERROR) << "Not enough free space on /cache"; + return false; } - if (SaveFileContents(CacheLocation::location().cache_temp_source().c_str(), &source_file) < 0) { - printf("failed to back up source file\n"); - return 1; + if (!SaveFileContents(Paths::Get().cache_temp_source(), &source_file)) { + LOG(ERROR) << "Failed to back up source file"; + return false; } // We store the decoded output in memory. - std::string memory_sink_str; // Don't need to reserve space. - SinkFn sink = [&memory_sink_str](const unsigned char* data, size_t len) { - memory_sink_str.append(reinterpret_cast(data), len); - return len; - }; - + FileContents patched; SHA_CTX ctx; SHA1_Init(&ctx); + SinkFn sink = [&patched, &ctx](const unsigned char* data, size_t len) { + SHA1_Update(&ctx, data, len); + patched.data.insert(patched.data.end(), data, data + len); + return len; + }; int result; if (use_bsdiff) { - result = - ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), *patch, 0, sink, &ctx); + result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch, 0, sink); } else { - result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), *patch, sink, &ctx, - bonus_data); + result = + ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch, sink, bonus_data); } if (result != 0) { - printf("applying patch failed\n"); - return 1; + LOG(ERROR) << "Failed to apply the patch: " << result; + return false; } - uint8_t current_target_sha1[SHA_DIGEST_LENGTH]; - SHA1_Final(current_target_sha1, &ctx); - if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) { - printf("patch did not produce expected sha1\n"); - return 1; - } else { - printf("now %s\n", short_sha1(target_sha1).c_str()); + SHA1_Final(patched.sha1, &ctx); + if (memcmp(patched.sha1, expected_sha1, SHA_DIGEST_LENGTH) != 0) { + LOG(ERROR) << "Patching did not produce the expected SHA-1 of " << short_sha1(expected_sha1); + + LOG(ERROR) << "target size " << patched.data.size() << " SHA-1 " << short_sha1(patched.sha1); + LOG(ERROR) << "source size " << source_file.data.size() << " SHA-1 " + << short_sha1(source_file.sha1); + + uint8_t patch_digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast(patch.data.data()), patch.data.size(), patch_digest); + LOG(ERROR) << "patch size " << patch.data.size() << " SHA-1 " << short_sha1(patch_digest); + + if (bonus_data != nullptr) { + uint8_t bonus_digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast(bonus_data->data.data()), bonus_data->data.size(), + bonus_digest); + LOG(ERROR) << "bonus size " << bonus_data->data.size() << " SHA-1 " + << short_sha1(bonus_digest); + } + + return false; } + LOG(INFO) << " now " << short_sha1(expected_sha1); + // Write back the temp file to the partition. - if (WriteToPartition(reinterpret_cast(memory_sink_str.c_str()), - memory_sink_str.size(), target_filename) != 0) { - printf("write of patched data to %s failed\n", target_filename.c_str()); - return 1; + if (*this) { + return "EMMC:"s + name + ":" + std::to_string(size) + ":" + hash; } + return ""; +} - // Delete the backup copy of the source. - unlink(CacheLocation::location().cache_temp_source().c_str()); - - // Success! - return 0; +std::ostream& operator<<(std::ostream& os, const Partition& partition) { + os << partition.ToString(); + return os; } diff --git a/applypatch/applypatch_main.cpp b/applypatch/applypatch_main.cpp index 197077c938..92d2b3fa9a 100644 --- a/applypatch/applypatch_main.cpp +++ b/applypatch/applypatch_main.cpp @@ -16,13 +16,10 @@ #include "applypatch_modes.h" -// This program (applypatch) applies binary patches to files in a way that -// is safe (the original file is not touched until we have the desired -// replacement for it) and idempotent (it's okay to run this program -// multiple times). -// -// See the comments to applypatch_modes() function. +#include +// See the comments for applypatch() function. int main(int argc, char** argv) { - return applypatch_modes(argc, const_cast(argv)); + android::base::InitLogging(argv); + return applypatch_modes(argc, argv); } diff --git a/applypatch/applypatch_modes.cpp b/applypatch/applypatch_modes.cpp index aa32d57ef4..b466598080 100644 --- a/applypatch/applypatch_modes.cpp +++ b/applypatch/applypatch_modes.cpp @@ -16,6 +16,7 @@ #include "applypatch_modes.h" +#include #include #include #include @@ -25,6 +26,8 @@ #include #include +#include +#include #include #include #include @@ -32,157 +35,153 @@ #include "applypatch/applypatch.h" #include "edify/expr.h" -static int CheckMode(int argc, const char** argv) { - if (argc < 3) { - return 2; - } - std::vector sha1; - for (int i = 3; i < argc; i++) { - sha1.push_back(argv[i]); - } +static int CheckMode(const std::string& target_emmc) { + std::string err; + auto target = Partition::Parse(target_emmc, &err); + if (!target) { + LOG(ERROR) << "Failed to parse target \"" << target_emmc << "\": " << err; + return 2; + } + return CheckPartition(target) ? 0 : 1; +} - return applypatch_check(argv[2], sha1); +static int FlashMode(const std::string& target_emmc, const std::string& source_file) { + std::string err; + auto target = Partition::Parse(target_emmc, &err); + if (!target) { + LOG(ERROR) << "Failed to parse target \"" << target_emmc << "\": " << err; + return 2; + } + return FlashPartition(target, source_file) ? 0 : 1; } -// Parse arguments (which should be of the form ":" into the -// new parallel arrays *sha1s and *files. Returns true on success. -static bool ParsePatchArgs(int argc, const char** argv, std::vector* sha1s, - std::vector* files) { - if (sha1s == nullptr) { - return false; +static int PatchMode(const std::string& target_emmc, const std::string& source_emmc, + const std::string& patch_file, const std::string& bonus_file) { + std::string err; + auto target = Partition::Parse(target_emmc, &err); + if (!target) { + LOG(ERROR) << "Failed to parse target \"" << target_emmc << "\": " << err; + return 2; + } + + auto source = Partition::Parse(source_emmc, &err); + if (!source) { + LOG(ERROR) << "Failed to parse source \"" << source_emmc << "\": " << err; + return 2; + } + + std::string patch_contents; + if (!android::base::ReadFileToString(patch_file, &patch_contents)) { + PLOG(ERROR) << "Failed to read patch file \"" << patch_file << "\""; + return 1; + } + + Value patch(Value::Type::BLOB, std::move(patch_contents)); + std::unique_ptr bonus; + if (!bonus_file.empty()) { + std::string bonus_contents; + if (!android::base::ReadFileToString(bonus_file, &bonus_contents)) { + PLOG(ERROR) << "Failed to read bonus file \"" << bonus_file << "\""; + return 1; } - for (int i = 0; i < argc; ++i) { - std::vector pieces = android::base::Split(argv[i], ":"); - if (pieces.size() != 2) { - printf("failed to parse patch argument \"%s\"\n", argv[i]); - return false; - } - - uint8_t digest[SHA_DIGEST_LENGTH]; - if (ParseSha1(pieces[0].c_str(), digest) != 0) { - printf("failed to parse sha1 \"%s\"\n", argv[i]); - return false; - } + bonus = std::make_unique(Value::Type::BLOB, std::move(bonus_contents)); + } - sha1s->push_back(pieces[0]); - FileContents fc; - if (LoadFileContents(pieces[1].c_str(), &fc) != 0) { - return false; - } - files->push_back(std::move(fc)); - } - return true; + return PatchPartition(target, source, patch, bonus.get()) ? 0 : 1; } -static int FlashMode(const char* src_filename, const char* tgt_filename, - const char* tgt_sha1, size_t tgt_size) { - return applypatch_flash(src_filename, tgt_filename, tgt_sha1, tgt_size); +static void Usage() { + printf( + "Usage: \n" + "check mode\n" + " applypatch --check EMMC:::\n\n" + "flash mode\n" + " applypatch --flash \n" + " --target EMMC:::\n\n" + "patch mode\n" + " applypatch [--bonus ]\n" + " --patch \n" + " --target EMMC:::\n" + " --source EMMC:::\n\n" + "show license\n" + " applypatch --license\n" + "\n\n"); } -static int PatchMode(int argc, const char** argv) { - FileContents bonusFc; - Value bonus(VAL_INVALID, ""); - - if (argc >= 3 && strcmp(argv[1], "-b") == 0) { - if (LoadFileContents(argv[2], &bonusFc) != 0) { - printf("failed to load bonus file %s\n", argv[2]); - return 1; +int applypatch_modes(int argc, char* argv[]) { + static constexpr struct option OPTIONS[]{ + // clang-format off + { "bonus", required_argument, nullptr, 0 }, + { "check", required_argument, nullptr, 0 }, + { "flash", required_argument, nullptr, 0 }, + { "license", no_argument, nullptr, 0 }, + { "patch", required_argument, nullptr, 0 }, + { "source", required_argument, nullptr, 0 }, + { "target", required_argument, nullptr, 0 }, + { nullptr, 0, nullptr, 0 }, + // clang-format on + }; + + std::string check_target; + std::string source; + std::string target; + std::string patch; + std::string bonus; + + bool check_mode = false; + bool flash_mode = false; + bool patch_mode = false; + + optind = 1; + + int arg; + int option_index; + while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { + switch (arg) { + case 0: { + std::string option = OPTIONS[option_index].name; + if (option == "bonus") { + bonus = optarg; + } else if (option == "check") { + check_target = optarg; + check_mode = true; + } else if (option == "flash") { + source = optarg; + flash_mode = true; + } else if (option == "license") { + return ShowLicenses(); + } else if (option == "patch") { + patch = optarg; + patch_mode = true; + } else if (option == "source") { + source = optarg; + } else if (option == "target") { + target = optarg; } - bonus.type = VAL_BLOB; - bonus.data = std::string(bonusFc.data.cbegin(), bonusFc.data.cend()); - argc -= 2; - argv += 2; - } - - if (argc < 4) { + break; + } + case '?': + default: + LOG(ERROR) << "Invalid argument"; + Usage(); return 2; } - - size_t target_size; - if (!android::base::ParseUint(argv[4], &target_size) || target_size == 0) { - printf("can't parse \"%s\" as byte count\n\n", argv[4]); - return 1; - } - - // If no : is provided, it is in flash mode. - if (argc == 5) { - if (bonus.type != VAL_INVALID) { - printf("bonus file not supported in flash mode\n"); - return 1; - } - return FlashMode(argv[1], argv[2], argv[3], target_size); - } - - std::vector sha1s; - std::vector files; - if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &files)) { - printf("failed to parse patch args\n"); - return 1; - } - - std::vector> patches; - for (size_t i = 0; i < files.size(); ++i) { - patches.push_back(std::make_unique( - VAL_BLOB, std::string(files[i].data.cbegin(), files[i].data.cend()))); - } - return applypatch(argv[1], argv[2], argv[3], target_size, sha1s, patches, &bonus); -} - -// This program (applypatch) applies binary patches to files in a way that -// is safe (the original file is not touched until we have the desired -// replacement for it) and idempotent (it's okay to run this program -// multiple times). -// -// - if the sha1 hash of is , does nothing and exits -// successfully. -// -// - otherwise, if no : is provided, flashes with -// . must be a partition name, while must -// be a regular image file. will not be deleted on success. -// -// - otherwise, if the sha1 hash of is , applies the -// bsdiff to to produce a new file (the type of patch -// is automatically detected from the file header). If that new -// file has sha1 hash , moves it to replace , and -// exits successfully. Note that if and are -// not the same, is NOT deleted on success. -// may be the string "-" to mean "the same as src-file". -// -// - otherwise, or if any error is encountered, exits with non-zero -// status. -// -// (or in check mode) may refer to an EMMC partition -// to read the source data. See the comments for the -// LoadPartitionContents() function for the format of such a filename. - -int applypatch_modes(int argc, const char** argv) { - if (argc < 2) { - usage: - printf( - "usage: %s [-b ] " - "[: ...]\n" - " or %s -c [ ...]\n" - " or %s -l\n" - "\n" - "Filenames may be of the form\n" - " EMMC::::::...\n" - "to specify reading from or writing to an EMMC partition.\n\n", - argv[0], argv[0], argv[0]); - return 2; - } - - int result; - - if (strncmp(argv[1], "-l", 3) == 0) { - result = ShowLicenses(); - } else if (strncmp(argv[1], "-c", 3) == 0) { - result = CheckMode(argc, argv); - } else { - result = PatchMode(argc, argv); - } - - if (result == 2) { - goto usage; + } + + if (check_mode) { + return CheckMode(check_target); + } + if (flash_mode) { + if (!bonus.empty()) { + LOG(ERROR) << "bonus file not supported in flash mode"; + return 1; } - return result; + return FlashMode(target, source); + } + if (patch_mode) { + return PatchMode(target, source, patch, bonus); + } + + Usage(); + return 2; } diff --git a/applypatch/applypatch_modes.h b/applypatch/applypatch_modes.h index 3d9d08df57..aa60a431f8 100644 --- a/applypatch/applypatch_modes.h +++ b/applypatch/applypatch_modes.h @@ -17,6 +17,6 @@ #ifndef _APPLYPATCH_MODES_H #define _APPLYPATCH_MODES_H -int applypatch_modes(int argc, const char** argv); +int applypatch_modes(int argc, char* argv[]); #endif // _APPLYPATCH_MODES_H diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index 912dbbdd8e..ba33c3a9c6 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -66,18 +66,12 @@ void ShowBSDiffLicense() { } int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch, - size_t patch_offset, SinkFn sink, SHA_CTX* ctx) { - auto sha_sink = [&sink, &ctx](const uint8_t* data, size_t len) { - len = sink(data, len); - if (ctx) SHA1_Update(ctx, data, len); - return len; - }; - + size_t patch_offset, SinkFn sink) { CHECK_LE(patch_offset, patch.data.size()); int result = bsdiff::bspatch(old_data, old_size, reinterpret_cast(&patch.data[patch_offset]), - patch.data.size() - patch_offset, sha_sink); + patch.data.size() - patch_offset, sink); if (result != 0) { LOG(ERROR) << "bspatch failed, result: " << result; // print SHA1 of the patch in the case of a data error. diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp index ea364d8e63..3868ef2306 100644 --- a/applypatch/freecache.cpp +++ b/applypatch/freecache.cpp @@ -14,31 +14,35 @@ * 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 +#include #include "applypatch/applypatch.h" -#include "otautil/cache_location.h" +#include "otautil/paths.h" -static int EliminateOpenFiles(std::set* files) { +static int EliminateOpenFiles(const std::string& dirname, std::set* files) { std::unique_ptr d(opendir("/proc"), closedir); if (!d) { - printf("error opening /proc: %s\n", strerror(errno)); + PLOG(ERROR) << "Failed to open /proc"; return -1; } struct dirent* de; @@ -52,7 +56,7 @@ static int EliminateOpenFiles(std::set* files) { struct dirent* fdde; std::unique_ptr fdd(opendir(path.c_str()), closedir); if (!fdd) { - printf("error opening %s: %s\n", path.c_str(), strerror(errno)); + PLOG(ERROR) << "Failed to open " << path; continue; } while ((fdde = readdir(fdd.get())) != 0) { @@ -62,9 +66,9 @@ static int EliminateOpenFiles(std::set* files) { int count = readlink(fd_path.c_str(), link, sizeof(link)-1); if (count >= 0) { link[count] = '\0'; - if (strncmp(link, "/cache/", 7) == 0) { + if (android::base::StartsWith(link, dirname)) { if (files->erase(link) > 0) { - printf("%s is open by %s\n", link, de->d_name); + LOG(INFO) << link << " is open by " << de->d_name; } } } @@ -73,77 +77,171 @@ static int EliminateOpenFiles(std::set* files) { return 0; } -static std::set FindExpendableFiles() { +static std::vector FindExpendableFiles( + const std::string& dirname, const std::function& name_filter) { + std::unique_ptr d(opendir(dirname.c_str()), closedir); + if (!d) { + PLOG(ERROR) << "Failed to open " << dirname; + return {}; + } + + // Look for regular files in the directory (not in any subdirectories). std::set files; - // We're allowed to delete unopened regular files in any of these - // directories. - const char* dirs[2] = {"/cache", "/cache/recovery/otatest"}; - - for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) { - std::unique_ptr d(opendir(dirs[i]), closedir); - if (!d) { - printf("error opening %s: %s\n", dirs[i], strerror(errno)); + struct dirent* de; + while ((de = readdir(d.get())) != 0) { + std::string path = dirname + "/" + de->d_name; + + // We can't delete cache_temp_source; if it's there we might have restarted during + // installation and could be depending on it to be there. + if (path == Paths::Get().cache_temp_source()) { continue; } - // Look for regular files in the directory (not in any subdirectories). - struct dirent* de; - while ((de = readdir(d.get())) != 0) { - std::string path = std::string(dirs[i]) + "/" + de->d_name; - - // We can't delete cache_temp_source; if it's there we might have restarted during - // installation and could be depending on it to be there. - if (path == CacheLocation::location().cache_temp_source()) { - continue; - } + // Do not delete the file if it doesn't have the expected format. + if (name_filter != nullptr && !name_filter(de->d_name)) { + continue; + } - struct stat st; - if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) { - files.insert(path); - } + struct stat st; + if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) { + files.insert(path); } } - printf("%zu regular files in deletable directories\n", files.size()); - if (EliminateOpenFiles(&files) < 0) { - return std::set(); + LOG(INFO) << files.size() << " regular files in deletable directory"; + if (EliminateOpenFiles(dirname, &files) < 0) { + return {}; } - return files; + + return std::vector(files.begin(), files.end()); } -int MakeFreeSpaceOnCache(size_t bytes_needed) { +// Parses the index of given log file, e.g. 3 for last_log.3; returns max number if the log name +// doesn't have the expected format so that we'll delete these ones first. +static unsigned int GetLogIndex(const std::string& log_name) { + if (log_name == "last_log" || log_name == "last_kmsg") { + return 0; + } + + unsigned int index; + if (sscanf(log_name.c_str(), "last_log.%u", &index) == 1 || + sscanf(log_name.c_str(), "last_kmsg.%u", &index) == 1) { + return index; + } + + return std::numeric_limits::max(); +} + +// Returns the amount of free space (in bytes) on the filesystem containing filename, or -1 on +// error. +static int64_t FreeSpaceForFile(const std::string& filename) { + struct statfs sf; + if (statfs(filename.c_str(), &sf) == -1) { + PLOG(ERROR) << "Failed to statfs " << filename; + return -1; + } + + auto f_bsize = static_cast(sf.f_bsize); + auto free_space = sf.f_bsize * sf.f_bavail; + if (f_bsize == 0 || free_space / f_bsize != static_cast(sf.f_bavail)) { + LOG(ERROR) << "Invalid block size or overflow (sf.f_bsize " << sf.f_bsize << ", sf.f_bavail " + << sf.f_bavail << ")"; + return -1; + } + return free_space; +} + +bool CheckAndFreeSpaceOnCache(size_t bytes) { #ifndef __ANDROID__ - // TODO (xunchang) implement a heuristic cache size check during host simulation. - printf("Skip making (%zu) bytes free space on cache; program is running on host\n", bytes_needed); - return 0; + // TODO(xunchang): Implement a heuristic cache size check during host simulation. + LOG(WARNING) << "Skipped making (" << bytes + << ") bytes free space on /cache; program is running on host"; + return true; #endif - size_t free_now = FreeSpaceForFile("/cache"); - printf("%zu bytes free on /cache (%zu needed)\n", free_now, bytes_needed); + std::vector dirs{ "/cache", Paths::Get().cache_log_directory() }; + for (const auto& dirname : dirs) { + if (RemoveFilesInDirectory(bytes, dirname, FreeSpaceForFile)) { + return true; + } + } - if (free_now >= bytes_needed) { - return 0; + return false; +} + +bool RemoveFilesInDirectory(size_t bytes_needed, const std::string& dirname, + const std::function& space_checker) { + // The requested size cannot exceed max int64_t. + if (static_cast(bytes_needed) > + static_cast(std::numeric_limits::max())) { + LOG(ERROR) << "Invalid arg of bytes_needed: " << bytes_needed; + return false; } - std::set files = FindExpendableFiles(); - if (files.empty()) { - // nothing we can delete to free up space! - printf("no files can be deleted to free space on /cache\n"); - return -1; + + struct stat st; + if (stat(dirname.c_str(), &st) == -1) { + PLOG(ERROR) << "Failed to stat " << dirname; + return false; + } + if (!S_ISDIR(st.st_mode)) { + LOG(ERROR) << dirname << " is not a directory"; + return false; } - // We could try to be smarter about which files to delete: the - // biggest ones? the smallest ones that will free up enough space? - // the oldest? the newest? - // - // Instead, we'll be dumb. + int64_t free_now = space_checker(dirname); + if (free_now == -1) { + LOG(ERROR) << "Failed to check free space for " << dirname; + return false; + } + LOG(INFO) << free_now << " bytes free on " << dirname << " (" << bytes_needed << " needed)"; + + if (free_now >= static_cast(bytes_needed)) { + return true; + } + + std::vector files; + if (dirname == Paths::Get().cache_log_directory()) { + // Deletes the log files only. + auto log_filter = [](const std::string& file_name) { + return android::base::StartsWith(file_name, "last_log") || + android::base::StartsWith(file_name, "last_kmsg"); + }; + + files = FindExpendableFiles(dirname, log_filter); + + // Older logs will come to the top of the queue. + auto comparator = [](const std::string& name1, const std::string& name2) -> bool { + unsigned int index1 = GetLogIndex(android::base::Basename(name1)); + unsigned int index2 = GetLogIndex(android::base::Basename(name2)); + if (index1 == index2) { + return name1 < name2; + } + + return index1 > index2; + }; + + std::sort(files.begin(), files.end(), comparator); + } else { + // We're allowed to delete unopened regular files in the directory. + files = FindExpendableFiles(dirname, nullptr); + } for (const auto& file : files) { - unlink(file.c_str()); - free_now = FreeSpaceForFile("/cache"); - printf("deleted %s; now %zu bytes free\n", file.c_str(), free_now); - if (free_now < bytes_needed) { - break; + if (unlink(file.c_str()) == -1) { + PLOG(ERROR) << "Failed to delete " << file; + continue; + } + + free_now = space_checker(dirname); + if (free_now == -1) { + LOG(ERROR) << "Failed to check free space for " << dirname; + return false; + } + LOG(INFO) << "Deleted " << file << "; now " << free_now << " bytes free"; + if (free_now >= static_cast(bytes_needed)) { + return true; } } - return (free_now >= bytes_needed) ? 0 : -1; + + return false; } diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 674cc2b16b..415d95f14e 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -462,12 +462,12 @@ PatchChunk::PatchChunk(const ImageChunk& tgt) target_len_(tgt.GetRawDataLength()), target_uncompressed_len_(tgt.DataLengthForPatch()), target_compress_level_(tgt.GetCompressLevel()), - data_(tgt.DataForPatch(), tgt.DataForPatch() + tgt.DataLengthForPatch()) {} + data_(tgt.GetRawData(), tgt.GetRawData() + tgt.GetRawDataLength()) {} // Return true if raw data is smaller than the patch size. bool PatchChunk::RawDataIsSmaller(const ImageChunk& tgt, size_t patch_size) { size_t target_len = tgt.GetRawDataLength(); - return (tgt.GetType() == CHUNK_NORMAL && (target_len <= 160 || target_len < patch_size)); + return target_len < patch_size || (tgt.GetType() == CHUNK_NORMAL && target_len <= 160); } void PatchChunk::UpdateSourceOffset(const SortedRangeSet& src_range) { diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 3682d61157..f4c33e5a31 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -38,6 +38,7 @@ #include #include "edify/expr.h" +#include "otautil/print_sha1.h" static inline int64_t Read8(const void *address) { return android::base::get_unaligned(address); @@ -51,8 +52,9 @@ static inline int32_t Read4(const void *address) { // patched data and stream the deflated data to output. static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_len, const Value& patch, size_t patch_offset, - const char* deflate_header, SinkFn sink, SHA_CTX* ctx) { + const char* deflate_header, SinkFn sink) { size_t expected_target_length = static_cast(Read8(deflate_header + 32)); + CHECK_GT(expected_target_length, static_cast(0)); int level = Read4(deflate_header + 40); int method = Read4(deflate_header + 44); int window_bits = Read4(deflate_header + 48); @@ -77,7 +79,7 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_ size_t total_written = 0; static constexpr size_t buffer_size = 32768; auto compression_sink = [&strm, &actual_target_length, &expected_target_length, &total_written, - &ret, &ctx, &sink](const uint8_t* data, size_t len) -> size_t { + &ret, &sink](const uint8_t* data, size_t len) -> size_t { // The input patch length for an update never exceeds INT_MAX. strm.avail_in = len; strm.next_in = data; @@ -102,15 +104,13 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_ LOG(ERROR) << "Failed to write " << have << " compressed bytes to output."; return 0; } - if (ctx) SHA1_Update(ctx, buffer.data(), have); } while ((strm.avail_in != 0 || strm.avail_out == 0) && ret != Z_STREAM_END); actual_target_length += len; return len; }; - int bspatch_result = - ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink, nullptr); + int bspatch_result = ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink); deflateEnd(&strm); if (bspatch_result != 0) { @@ -127,19 +127,20 @@ static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_ << actual_target_length; return false; } - LOG(DEBUG) << "bspatch writes " << total_written << " bytes in total to streaming output."; + LOG(DEBUG) << "bspatch wrote " << total_written << " bytes in total to streaming output."; return true; } int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, size_t patch_size, SinkFn sink) { - Value patch(VAL_BLOB, std::string(reinterpret_cast(patch_data), patch_size)); - return ApplyImagePatch(old_data, old_size, patch, sink, nullptr, nullptr); + Value patch(Value::Type::BLOB, + std::string(reinterpret_cast(patch_data), patch_size)); + return ApplyImagePatch(old_data, old_size, patch, sink, nullptr); } int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink, - SHA_CTX* ctx, const Value* bonus_data) { + const Value* bonus_data) { if (patch.data.size() < 12) { printf("patch too short to contain header\n"); return -1; @@ -180,10 +181,12 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& printf("source data too short\n"); return -1; } - if (ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx) != 0) { + if (ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink) != 0) { printf("Failed to apply bsdiff patch.\n"); return -1; } + + LOG(DEBUG) << "Processed chunk type normal"; } else if (type == CHUNK_RAW) { const char* raw_header = patch_header + pos; pos += 4; @@ -198,14 +201,13 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& printf("failed to read chunk %d raw data\n", i); return -1; } - if (ctx) { - SHA1_Update(ctx, patch_header + pos, data_len); - } if (sink(reinterpret_cast(patch_header + pos), data_len) != data_len) { printf("failed to write chunk %d raw data\n", i); return -1; } pos += data_len; + + LOG(DEBUG) << "Processed chunk type raw"; } else if (type == CHUNK_DEFLATE) { // deflate chunks have an additional 60 bytes in their chunk header. const char* deflate_header = patch_header + pos; @@ -228,11 +230,10 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& // Decompress the source data; the chunk header tells us exactly // how big we expect it to be when decompressed. - // Note: expanded_len will include the bonus data size if - // the patch was constructed with bonus data. The - // deflation will come up 'bonus_size' bytes short; these - // must be appended from the bonus_data value. - size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->data.size() : 0; + // Note: expanded_len will include the bonus data size if the patch was constructed with + // bonus data. The deflation will come up 'bonus_size' bytes short; these must be appended + // from the bonus_data value. + size_t bonus_size = (i == 1 && bonus_data != nullptr) ? bonus_data->data.size() : 0; std::vector expanded_source(expanded_len); @@ -270,17 +271,18 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& inflateEnd(&strm); if (bonus_size) { - memcpy(expanded_source.data() + (expanded_len - bonus_size), &bonus_data->data[0], + memcpy(expanded_source.data() + (expanded_len - bonus_size), bonus_data->data.data(), bonus_size); } } if (!ApplyBSDiffPatchAndStreamOutput(expanded_source.data(), expanded_len, patch, - patch_offset, deflate_header, sink, ctx)) { + patch_offset, deflate_header, sink)) { LOG(ERROR) << "Fail to apply streaming bspatch."; return -1; } + LOG(DEBUG) << "Processed chunk type deflate"; } else { printf("patch chunk %d is unknown type %d\n", i, type); return -1; diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 912ead1fa0..6fc6f0fc91 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -39,45 +40,91 @@ using SinkFn = std::function; // applypatch.cpp int ShowLicenses(); -size_t FreeSpaceForFile(const char* filename); -int CacheSizeCheck(size_t bytes); -int ParseSha1(const char* str, uint8_t* digest); - -int applypatch(const char* source_filename, - const char* target_filename, - const char* target_sha1_str, - size_t target_size, - const std::vector& patch_sha1_str, - const std::vector>& patch_data, - const Value* bonus_data); -int applypatch_check(const char* filename, - const std::vector& patch_sha1_str); -int applypatch_flash(const char* source_filename, const char* target_filename, - const char* target_sha1_str, size_t target_size); - -int LoadFileContents(const char* filename, FileContents* file); -int SaveFileContents(const char* filename, const FileContents* file); + +// Parses a given string of 40 hex digits into 20-byte array 'digest'. 'str' may contain only the +// digest or be of the form ":". Returns 0 on success, or -1 on any error. +int ParseSha1(const std::string& str, uint8_t* digest); + +struct Partition { + Partition() = default; + + Partition(const std::string& name, size_t size, const std::string& hash) + : name(name), size(size), hash(hash) {} + + // Parses and returns the given string into a Partition object. The input string is of the form + // "EMMC:::". Returns the parsed Partition, or an empty object on error. + static Partition Parse(const std::string& partition, std::string* err); + + std::string ToString() const; + + // Returns whether the current Partition object is valid. + explicit operator bool() const { + return !name.empty(); + } + + std::string name; + size_t size; + std::string hash; +}; + +std::ostream& operator<<(std::ostream& os, const Partition& partition); + +// Applies the given 'patch' to the 'source' Partition, verifies then writes the patching result to +// the 'target' Partition. While patching, it will backup the data on the source partition to +// /cache, so that the patching could be resumed on interruption even if both of the source and +// target partitions refer to the same device. The function is idempotent if called multiple times. +// An optional arg 'bonus' can be provided, if the patch was generated with a bonus output. +// Returns the patching result. +bool PatchPartition(const Partition& target, const Partition& source, const Value& patch, + const Value* bonus); + +// Returns whether the contents of the eMMC target or the cached file match the embedded hash. +// It will look for the backup on /cache if the given partition doesn't match the checksum. +bool PatchPartitionCheck(const Partition& target, const Partition& source); + +// Checks whether the contents of the given partition has the desired hash. It will NOT look for +// the backup on /cache if the given partition doesn't have the expected checksum. +bool CheckPartition(const Partition& target); + +// Flashes a given image in 'source_filename' to the eMMC target partition. It verifies the target +// checksum first, and will return if target already has the desired hash. Otherwise it checks the +// checksum of the given source image, flashes, and verifies the target partition afterwards. The +// function is idempotent. Returns the flashing result. +bool FlashPartition(const Partition& target, const std::string& source_filename); + +// Reads a file into memory; stores the file contents and associated metadata in *file. +bool LoadFileContents(const std::string& filename, FileContents* file); + +// Saves the given FileContents object to the given filename. +bool SaveFileContents(const std::string& filename, const FileContents* file); // bspatch.cpp void ShowBSDiffLicense(); // Applies the bsdiff-patch given in 'patch' (from offset 'patch_offset' to the end) to the source -// data given by (old_data, old_size). Writes the patched output through the given 'sink', and -// updates the SHA-1 context with the output data. Returns 0 on success. +// data given by (old_data, old_size). Writes the patched output through the given 'sink'. Returns +// 0 on success. int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value& patch, - size_t patch_offset, SinkFn sink, SHA_CTX* ctx); + size_t patch_offset, SinkFn sink); // imgpatch.cpp // Applies the imgdiff-patch given in 'patch' to the source data given by (old_data, old_size), with -// the optional bonus data. Writes the patched output through the given 'sink', and updates the -// SHA-1 context with the output data. Returns 0 on success. +// the optional bonus data. Writes the patched output through the given 'sink'. Returns 0 on +// success. int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value& patch, SinkFn sink, - SHA_CTX* ctx, const Value* bonus_data); + const Value* bonus_data); // freecache.cpp -int MakeFreeSpaceOnCache(size_t bytes_needed); +// Checks whether /cache partition has at least 'bytes'-byte free space. Returns true immediately +// if so. Otherwise, it will try to free some space by removing older logs, checks again and +// returns the checking result. +bool CheckAndFreeSpaceOnCache(size_t bytes); +// Removes the files in |dirname| until we have at least |bytes_needed| bytes of free space on the +// partition. |space_checker| should return the size of the free space, or -1 on error. +bool RemoveFilesInDirectory(size_t bytes_needed, const std::string& dirname, + const std::function& space_checker); #endif diff --git a/applypatch/include/applypatch/imgdiff_image.h b/applypatch/include/applypatch/imgdiff_image.h index 0848072374..671605160a 100644 --- a/applypatch/include/applypatch/imgdiff_image.h +++ b/applypatch/include/applypatch/imgdiff_image.h @@ -44,6 +44,8 @@ class ImageChunk { int GetType() const { return type_; } + + const uint8_t* GetRawData() const; size_t GetRawDataLength() const { return raw_data_len_; } @@ -99,7 +101,6 @@ class ImageChunk { bsdiff::SuffixArrayIndexInterface** bsdiff_cache); private: - const uint8_t* GetRawData() const; bool TryReconstruction(int level); int type_; // CHUNK_NORMAL, CHUNK_DEFLATE, CHUNK_RAW diff --git a/attr/Android.mk b/attr/Android.mk old mode 100644 new mode 100755 index 2986ab50bf..121ae02d36 --- a/attr/Android.mk +++ b/attr/Android.mk @@ -4,19 +4,19 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := listxattr.c LOCAL_CFLAGS := -c -W LOCAL_MODULE := listxattr -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/ LOCAL_PACK_MODULE_RELOCATIONS := false ifneq ($(TARGET_ARCH), arm64) ifneq ($(TARGET_ARCH), x86_64) - LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker + LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker else - LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64 endif else - LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64 endif include $(BUILD_EXECUTABLE) diff --git a/bmlutils/Android.mk b/bmlutils/Android.mk index 7216775d5d..9cbada0290 100644 --- a/bmlutils/Android.mk +++ b/bmlutils/Android.mk @@ -13,7 +13,7 @@ $(foreach board_define,$(BOARD_RECOVERY_DEFINES), \ LOCAL_SRC_FILES := bmlutils.c LOCAL_MODULE := libbmlutils -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional include $(BUILD_STATIC_LIBRARY) #Added for building TWRP dynamic: @@ -30,5 +30,5 @@ $(foreach board_define,$(BOARD_RECOVERY_DEFINES), \ LOCAL_SRC_FILES := bmlutils.c LOCAL_MODULE := libbmlutils -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) diff --git a/bmlutils/bmlutils.c b/bmlutils/bmlutils.c old mode 100644 new mode 100755 index b5da078945..382c0a5fed --- a/bmlutils/bmlutils.c +++ b/bmlutils/bmlutils.c @@ -180,14 +180,14 @@ int format_rfs_device (const char *device, const char *path) { // dump 10KB of zeros to partition before format due to fat.format bug char cmd[PATH_MAX]; - sprintf(cmd, "/sbin/dd if=/dev/zero of=%s bs=4096 count=10", device); + sprintf(cmd, "/system/bin/dd if=/dev/zero of=%s bs=4096 count=10", device); if(__system(cmd)) { printf("failure while zeroing rfs partition.\n"); return -1; } // Run fat.format - sprintf(cmd, "/sbin/fat.format -F %s -S 4096 -s %s %s", fatsize, sectorsize, device); + sprintf(cmd, "/system/bin/fat.format -F %s -S 4096 -s %s %s", fatsize, sectorsize, device); if(__system(cmd)) { printf("failure while running fat.format\n"); return -1; diff --git a/boot_control/Android.bp b/boot_control/Android.bp new file mode 100644 index 0000000000..7720ead50f --- /dev/null +++ b/boot_control/Android.bp @@ -0,0 +1,37 @@ +// +// 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. +// + +cc_library_shared { + name: "bootctrl.default", + recovery_available: true, + relative_install_path: "hw", + + srcs: ["boot_control.cpp"], + + cflags: [ + "-D_FILE_OFFSET_BITS=64", + "-Werror", + "-Wall", + "-Wextra", + ], + + shared_libs: [ + "libbase", + "libbootloader_message", + "libfs_mgr", + "liblog", + ], +} diff --git a/boot_control/Android.mk b/boot_control/Android.mk deleted file mode 100644 index 9814d71222..0000000000 --- a/boot_control/Android.mk +++ /dev/null @@ -1,33 +0,0 @@ -# -# 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. -# - -LOCAL_PATH := $(my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := bootctrl.bcb -LOCAL_MODULE_RELATIVE_PATH := hw -LOCAL_SRC_FILES := boot_control.cpp -LOCAL_CFLAGS := \ - -D_FILE_OFFSET_BITS=64 \ - -Werror \ - -Wall \ - -Wextra -LOCAL_SHARED_LIBRARIES := liblog -LOCAL_STATIC_LIBRARIES := libbootloader_message libfs_mgr libbase -LOCAL_POST_INSTALL_CMD := \ - $(hide) mkdir -p $(TARGET_OUT_SHARED_LIBRARIES)/hw && \ - ln -sf bootctrl.bcb.so $(TARGET_OUT_SHARED_LIBRARIES)/hw/bootctrl.default.so -include $(BUILD_SHARED_LIBRARY) diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index c81c67bdbd..450dad08b4 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -14,16 +14,34 @@ // limitations under the License. // -cc_library_static { - name: "libbootloader_message", +cc_defaults { + name: "libbootloader_message_defaults", srcs: ["bootloader_message.cpp"], cflags: [ "-Wall", "-Werror", ], - static_libs: [ + shared_libs: [ "libbase", - "libfs_mgr", + ], + static_libs: [ + "libfstab", ], export_include_dirs: ["include"], } + +cc_library { + name: "libbootloader_message", + defaults: [ + "libbootloader_message_defaults", + ], + recovery_available: true, +} + +cc_library_static { + name: "libbootloader_message_vendor", + defaults: [ + "libbootloader_message_defaults", + ], + vendor: true, +} diff --git a/bootloader_message/Android.mk b/bootloader_message/Android.mk deleted file mode 100644 index 0d84713c32..0000000000 --- a/bootloader_message/Android.mk +++ /dev/null @@ -1,29 +0,0 @@ -# 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. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_CLANG := true -LOCAL_SRC_FILES := bootloader_message.cpp -LOCAL_MODULE := libbootloader_message -LOCAL_STATIC_LIBRARIES := libbase libfs_mgr -LOCAL_CFLAGS := -Werror -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0) - TARGET_GLOBAL_CFLAGS += -DUSE_OLD_BOOTLOADER_MESSAGE - CLANG_TARGET_GLOBAL_CFLAGS += -DUSE_OLD_BOOTLOADER_MESSAGE -endif -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include -include $(BUILD_STATIC_LIBRARY) diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp old mode 100644 new mode 100755 index 72ec8bcc9a..c1ebeaa82f --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -21,54 +21,42 @@ #include #include +#include #include #include +#include #include #include -#include - -#ifdef USE_OLD_BOOTLOADER_MESSAGE -#include - -static struct fstab* read_fstab(std::string* err) { - // The fstab path is always "/fstab.${ro.hardware}". - std::string fstab_path = "/fstab."; - char value[PROP_VALUE_MAX]; - if (__system_property_get("ro.hardware", value) == 0) { - *err = "failed to get ro.hardware"; - return nullptr; - } - fstab_path += value; - struct fstab* fstab = fs_mgr_read_fstab(fstab_path.c_str()); - if (fstab == nullptr) { - *err = "failed to read " + fstab_path; - } - return fstab; +#include + +using android::fs_mgr::Fstab; +using android::fs_mgr::ReadDefaultFstab; + +static std::string g_misc_device_for_test; + +// Exposed for test purpose. +void SetMiscBlockDeviceForTest(std::string_view misc_device) { + g_misc_device_for_test = misc_device; } -#endif static std::string get_misc_blk_device(std::string* err) { -#ifdef USE_OLD_BOOTLOADER_MESSAGE - struct fstab* fstab = read_fstab(err); -#else - std::unique_ptr fstab(fs_mgr_read_fstab_default(), - fs_mgr_free_fstab); -#endif - if (!fstab) { + if (!g_misc_device_for_test.empty()) { + return g_misc_device_for_test; + } + Fstab fstab; + if (!ReadDefaultFstab(&fstab)) { *err = "failed to read default fstab"; return ""; } -#ifdef USE_OLD_BOOTLOADER_MESSAGE - fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab, "/misc"); -#else - fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab.get(), "/misc"); -#endif - if (record == nullptr) { - *err = "failed to find /misc partition"; - return ""; + for (const auto& entry : fstab) { + if (entry.mount_point == "/misc") { + return entry.blk_device; + } } - return record->blk_device; + + *err = "failed to find /misc partition"; + return ""; } // In recovery mode, recovery can get started and try to access the misc @@ -251,6 +239,37 @@ bool write_wipe_package(const std::string& package_data, std::string* err) { WIPE_PACKAGE_OFFSET_IN_MISC, err); } +static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size) { + auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC; + return size <= total_size && offset <= total_size - size; +} + +bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err) { + if (!OffsetAndSizeInVendorSpace(offset, size)) { + *err = android::base::StringPrintf("Out of bound read (offset %zu size %zu)", offset, size); + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return read_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, + err); +} + +bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err) { + if (!OffsetAndSizeInVendorSpace(offset, size)) { + *err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size); + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, + err); +} + extern "C" bool write_reboot_bootloader(void) { std::string err; return write_reboot_bootloader(&err); diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 29f5601851..95dd8f4c9b 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -28,8 +28,9 @@ // 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices // Note that these offsets are admitted by bootloader,recovery and uncrypt, so they // are not configurable without changing all of them. -static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; -static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; +constexpr size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; +constexpr size_t VENDOR_SPACE_OFFSET_IN_MISC = 2 * 1024; +constexpr size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; /* Bootloader Message (2-KiB) * @@ -65,16 +66,6 @@ struct bootloader_message { char status[32]; char recovery[768]; -#ifdef USE_OLD_BOOTLOADER_MESSAGE - // The 'recovery' field used to be 1024 bytes. It has only ever - // been used to store the recovery command line, so 768 bytes - // should be plenty. We carve off the last 256 bytes to store the - // stage string (for multistage packages) and possible future - // expansion. - char stage[32]; - char slot_suffix[32]; - char reserved[192]; -#else // The 'recovery' field used to be 1024 bytes. It has only ever // been used to store the recovery command line, so 768 bytes // should be plenty. We carve off the last 256 bytes to store the @@ -87,14 +78,13 @@ struct bootloader_message { // 1184-byte so that the entire bootloader_message struct rounds up // to 2048-byte. char reserved[1184]; -#endif }; /** * We must be cautious when changing the bootloader_message struct size, * because A/B-specific fields may end up with different offsets. */ -#if !defined(USE_OLD_BOOTLOADER_MESSAGE) && ((__STDC_VERSION__ >= 201112L) || defined(__cplusplus)) +#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) static_assert(sizeof(struct bootloader_message) == 2048, "struct bootloader_message size changes, which may break A/B devices"); #endif @@ -131,7 +121,7 @@ struct bootloader_message_ab { * Be cautious about the struct size change, in case we put anything post * bootloader_message_ab struct (b/29159185). */ -#if !defined(USE_OLD_BOOTLOADER_MESSAGE) && ((__STDC_VERSION__ >= 201112L) || defined(__cplusplus)) +#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) static_assert(sizeof(struct bootloader_message_ab) == 4096, "struct bootloader_message_ab size changes"); #endif @@ -239,6 +229,14 @@ bool read_wipe_package(std::string* package_data, size_t size, std::string* err) // Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC). bool write_wipe_package(const std::string& package_data, std::string* err); +// Reads data from the vendor space in /misc partition, with the given offset and size. Note that +// offset is in relative to the start of vendor space. +bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err); + +// Writes the given data to the vendor space in /misc partition, at the given offset. Note that +// offset is in relative to the start of the vendor space. +bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err); + #else #include diff --git a/bootloader_message_twrp/Android.mk b/bootloader_message_twrp/Android.mk deleted file mode 100644 index e7a3ea0f8c..0000000000 --- a/bootloader_message_twrp/Android.mk +++ /dev/null @@ -1,35 +0,0 @@ -# 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. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_CLANG := true -LOCAL_SRC_FILES := bootloader_message.cpp -LOCAL_MODULE := libbootloader_message_twrp -LOCAL_C_INCLUDES += bionic $(LOCAL_PATH)/include -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 21; echo $$?),0) - LOCAL_C_INCLUDES += external/stlport/stlport - LOCAL_SHARED_LIBRARIES += libstlport -else - LOCAL_C_INCLUDES += external/libcxx/include - LOCAL_SHARED_LIBRARIES += libc++ -endif -LOCAL_CFLAGS := -Werror -std=c++11 -# ignore bootloader's factory reset command even when written to /misc -ifeq ($(TW_IGNORE_MISC_WIPE_DATA), true) - LOCAL_CFLAGS += -DIGNORE_MISC_WIPE_DATA -endif -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include -include $(BUILD_SHARED_LIBRARY) diff --git a/bootloader_message_twrp/bootloader_message.cpp b/bootloader_message_twrp/bootloader_message.cpp deleted file mode 100644 index a06ad9a89f..0000000000 --- a/bootloader_message_twrp/bootloader_message.cpp +++ /dev/null @@ -1,386 +0,0 @@ -/* - * 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 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static std::string misc_blkdev; - -void set_misc_device(const char* name) { - misc_blkdev = name; -} - -static std::string get_misc_blk_device(std::string* err) { - *err = ""; - return misc_blkdev; -} - -// In recovery mode, recovery can get started and try to access the misc -// device before the kernel has actually created it. -static bool wait_for_device(const std::string& blk_device, std::string* err) { - int tries = 0; - int ret; - err->clear(); - do { - ++tries; - struct stat buf; - ret = stat(blk_device.c_str(), &buf); - if (ret == -1) { - char buffer[2048]; - sprintf(buffer, "failed to stat %s try %d: %s\n", - blk_device.c_str(), tries, strerror(errno)); - *err += buffer; - /* - *err += android::base::StringPrintf("failed to stat %s try %d: %s\n", - blk_device.c_str(), tries, strerror(errno)); - */ - sleep(1); - } - } while (ret && tries < 10); - - if (ret) { - *err += "failed to stat " + blk_device + "\n"; - /* - *err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str()); - */ - } - return ret == 0; -} - -static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device, - size_t offset, std::string* err) { - if (!wait_for_device(misc_blk_device, err)) { - return false; - } - - int fd(open(misc_blk_device.c_str(), O_RDONLY)); - if (fd < 0) { - *err = "failed to open " + misc_blk_device + ": "; - *err += strerror(errno); - /* - android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY)); - if (fd == -1) { - *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(), - strerror(errno)); - */ - return false; - } - if (lseek(fd, static_cast(offset), SEEK_SET) != static_cast(offset)) { - *err = "failed to lseek " + misc_blk_device + ": "; - *err += strerror(errno); - close(fd); - /* - - *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(), - strerror(errno)); - */ - return false; - } - - if ((size_t)read(fd, p, size) != size) { - *err = "failed to read " + misc_blk_device + ": "; - *err += strerror(errno); - close(fd); - /* - if (!android::base::ReadFully(fd, p, size)) { - *err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(), - strerror(errno)); - */ - return false; - } - close(fd); - return true; -} - -static bool write_misc_partition(const void* p, size_t size, size_t offset, std::string* err) { - std::string misc_blk_device = get_misc_blk_device(err); - if (misc_blk_device.empty()) { - *err = "no misc device set"; - return false; - } - int fd = (open(misc_blk_device.c_str(), O_WRONLY | O_SYNC)); - if (fd == -1) { - *err = "failed to open " + misc_blk_device + ": "; - *err += strerror(errno); - /* -static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device, - size_t offset, std::string* err) { - android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY)); - if (fd == -1) { - *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(), - strerror(errno)); - */ - return false; - } - if (lseek(fd, static_cast(offset), SEEK_SET) != static_cast(offset)) { - *err = "failed to lseek " + misc_blk_device + ": "; - *err += strerror(errno); - close(fd); - /* - *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(), - strerror(errno)); - */ - return false; - } - if ((size_t)write(fd, p, size) != size) { - *err = "failed to write " + misc_blk_device + ": "; - *err += strerror(errno); - close(fd); - /* - if (!android::base::WriteFully(fd, p, size)) { - *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(), - strerror(errno)); - */ - return false; - } - - // TODO: O_SYNC and fsync duplicates each other? - if (fsync(fd) == -1) { - *err = "failed to fsync " + misc_blk_device + ": "; - *err += strerror(errno); - close(fd); - /* - if (fsync(fd) == -1) { - *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(), - strerror(errno)); - */ - return false; - } - close(fd); - return true; -} - -bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device, - std::string* err) { - return read_misc_partition(boot, sizeof(*boot), misc_blk_device, - BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err); -} - -bool read_bootloader_message(bootloader_message* boot, std::string* err) { - std::string misc_blk_device = get_misc_blk_device(err); - if (misc_blk_device.empty()) { - return false; - } - return read_bootloader_message_from(boot, misc_blk_device, err); -} - -bool write_bootloader_message_to(const bootloader_message& boot, __unused const std::string& misc_blk_device, - std::string* err) { - return write_misc_partition(&boot, sizeof(boot), - BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err); -} - -bool write_bootloader_message(const bootloader_message& boot, std::string* err) { - std::string misc_blk_device = get_misc_blk_device(err); - if (misc_blk_device.empty()) { - return false; - } - return write_bootloader_message_to(boot, misc_blk_device, err); -} - -// libc++ in 5.1 does not know how to handle a std::string* so this craziness is needed -bool clear_bootloader_message(void* err) { - std::string &s = *(static_cast(err)); - return clear_bootloader_message(&s); -} - -bool clear_bootloader_message(std::string* err) { - bootloader_message boot = {}; - return write_bootloader_message(boot, err); -} - -bool write_bootloader_message(const std::vector& options, std::string* err) { - bootloader_message boot = {}; - strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); - strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); - for (const auto& s : options) { - strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery)); - if (s.substr(s.size() - 1) != "\n") { - strlcat(boot.recovery, "\n", sizeof(boot.recovery)); - } - } - return write_bootloader_message(boot, err); -} - -bool update_bootloader_message(const std::vector& options, std::string* err) { - bootloader_message boot; - if (!read_bootloader_message(&boot, err)) { - return false; - } - - // Zero out the entire fields. - memset(boot.command, 0, sizeof(boot.command)); - memset(boot.recovery, 0, sizeof(boot.recovery)); - - strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); - strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); - for (const auto& s : options) { - strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery)); - if (s.back() != '\n') { - strlcat(boot.recovery, "\n", sizeof(boot.recovery)); - } - } - return write_bootloader_message(boot, err); -} - -bool write_reboot_bootloader(std::string* err) { - bootloader_message boot; - if (!read_bootloader_message(&boot, err)) { - return false; - } - if (boot.command[0] != '\0') { - *err = "Bootloader command pending."; - return false; - } - strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command)); - return write_bootloader_message(boot, err); -} - -bool read_wipe_package(std::string* package_data, size_t size, std::string* err) { - std::string misc_blk_device = get_misc_blk_device(err); - if (misc_blk_device.empty()) { - return false; - } - package_data->resize(size); - return read_misc_partition(&(*package_data)[0], size, misc_blk_device, - WIPE_PACKAGE_OFFSET_IN_MISC, err); -} - -bool write_wipe_package(const std::string& package_data, std::string* err) { - std::string misc_blk_device = get_misc_blk_device(err); - if (misc_blk_device.empty()) { - return false; - } - return write_misc_partition(package_data.data(), package_data.size(), - WIPE_PACKAGE_OFFSET_IN_MISC, err); -} - -extern "C" bool write_reboot_bootloader(void) { - std::string err; - return write_reboot_bootloader(&err); -} - -extern "C" bool write_bootloader_message(const char* options) { - std::string err; - bootloader_message boot = {}; - memcpy(&boot, options, sizeof(boot)); - return write_bootloader_message(boot, &err); -} - -static const char *COMMAND_FILE = "/cache/recovery/command"; -static const int MAX_ARG_LENGTH = 4096; -static const int MAX_ARGS = 100; - -// command line args come from, in decreasing precedence: -// - the actual command line -// - the bootloader control block (one per line, after "recovery") -// - the contents of COMMAND_FILE (one per line) -void -get_args(int *argc, char ***argv) { - bootloader_message boot = {}; - std::string err; - if (!read_bootloader_message(&boot, &err)) { - printf("%s\n", err.c_str()); - // If fails, leave a zeroed bootloader_message. - memset(&boot, 0, sizeof(boot)); - } - //stage = strndup(boot.stage, sizeof(boot.stage)); - - if (boot.command[0] != 0 && boot.command[0] != (char)255) { - printf("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command); - } - - if (boot.status[0] != 0 && boot.status[0] != (char)255) { - printf("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status); - } - - // --- if arguments weren't supplied, look in the bootloader control block - if (*argc <= 1) { - boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination - const char *arg = strtok(boot.recovery, "\n"); - if (arg != NULL && !strcmp(arg, "recovery")) { - *argv = (char **) malloc(sizeof(char *) * MAX_ARGS); - (*argv)[0] = strdup(arg); - for (*argc = 1; *argc < MAX_ARGS; ++*argc) { - if ((arg = strtok(NULL, "\n")) == NULL) break; - -// if the device does not have an own recovery key combo we just want to open TWRP after -// walking through the factory reset screen - without actually doing a factory reset -#ifdef IGNORE_MISC_WIPE_DATA - if (!strcmp(arg, "--wipe_data")) { - (*argv)[*argc] = NULL; - *argc = *argc -1; - printf("Bootloader arg \"%s\" ignored because TWRP was compiled with TW_IGNORE_MISC_WIPE_DATA\n", arg); - continue; - } -#endif - (*argv)[*argc] = strdup(arg); - } - printf("Got arguments from boot message\n"); - } else if (boot.recovery[0] != 0 && boot.recovery[0] != (char)255) { - printf("Bad boot message\n\"%.20s\"\n", boot.recovery); - } - } - - // --- if that doesn't work, try the command file (if we have /cache). - if (*argc <= 1/* && has_cache*/) { - FILE *fp = fopen(COMMAND_FILE, "r"); - if (fp != NULL) { - char *token; - char *argv0 = (*argv)[0]; - *argv = (char **) malloc(sizeof(char *) * MAX_ARGS); - (*argv)[0] = argv0; // use the same program name - - char buf[MAX_ARG_LENGTH]; - for (*argc = 1; *argc < MAX_ARGS; ++*argc) { - if (!fgets(buf, sizeof(buf), fp)) break; - token = strtok(buf, "\r\n"); - if (token != NULL) { - (*argv)[*argc] = strdup(token); // Strip newline. - } else { - --*argc; - } - } - - fclose(fp); - printf("Got arguments from %s\n", COMMAND_FILE); - } - } - - // --> write the arguments we have back into the bootloader control block - // always boot into recovery after this (until finish_recovery() is called) - strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); - strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); - int i; - for (i = 1; i < *argc; ++i) { - strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery)); - strlcat(boot.recovery, "\n", sizeof(boot.recovery)); - } - if (!write_bootloader_message(boot, &err)) { - printf("%s\n", err.c_str()); - } -} diff --git a/bootloader_message_twrp/include/bootloader_message_twrp/bootloader_message.h b/bootloader_message_twrp/include/bootloader_message_twrp/bootloader_message.h deleted file mode 100644 index 52c1b86f0e..0000000000 --- a/bootloader_message_twrp/include/bootloader_message_twrp/bootloader_message.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 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 _BOOTLOADER_MESSAGE_TWRP_H -#define _BOOTLOADER_MESSAGE_TWRP_H - -#include -#include -#include - -// Spaces used by misc partition are as below: -// 0 - 2K For bootloader_message -// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used -// as bootloader_message_ab struct) -// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices -// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they -// are not configurable without changing all of them. -#ifdef BOARD_RECOVERY_BLDRMSG_OFFSET -static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = BOARD_RECOVERY_BLDRMSG_OFFSET; -static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024 + BOOTLOADER_MESSAGE_OFFSET_IN_MISC; -#else -static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; -static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; -#endif - -/* Bootloader Message (2-KiB) - * - * This structure describes the content of a block in flash - * that is used for recovery and the bootloader to talk to - * each other. - * - * The command field is updated by linux when it wants to - * reboot into recovery or to update radio or bootloader firmware. - * It is also updated by the bootloader when firmware update - * is complete (to boot into recovery for any final cleanup) - * - * The status field was used by the bootloader after the completion - * of an "update-radio" or "update-hboot" command, which has been - * deprecated since Froyo. - * - * The recovery field is only written by linux and used - * for the system to send a message to recovery or the - * other way around. - * - * The stage field is written by packages which restart themselves - * multiple times, so that the UI can reflect which invocation of the - * package it is. If the value is of the format "#/#" (eg, "1/3"), - * the UI will add a simple indicator of that status. - * - * We used to have slot_suffix field for A/B boot control metadata in - * this struct, which gets unintentionally cleared by recovery or - * uncrypt. Move it into struct bootloader_message_ab to avoid the - * issue. - */ -struct bootloader_message { - char command[32]; - char status[32]; - char recovery[768]; - - // The 'recovery' field used to be 1024 bytes. It has only ever - // been used to store the recovery command line, so 768 bytes - // should be plenty. We carve off the last 256 bytes to store the - // stage string (for multistage packages) and possible future - // expansion. - char stage[32]; - - // The 'reserved' field used to be 224 bytes when it was initially - // carved off from the 1024-byte recovery field. Bump it up to - // 1184-byte so that the entire bootloader_message struct rounds up - // to 2048-byte. - char reserved[1184]; -}; - -/** - * We must be cautious when changing the bootloader_message struct size, - * because A/B-specific fields may end up with different offsets. - */ -/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) -static_assert(sizeof(struct bootloader_message) == 2048, - "struct bootloader_message size changes, which may break A/B devices"); -#endif*/ - -/** - * The A/B-specific bootloader message structure (4-KiB). - * - * We separate A/B boot control metadata from the regular bootloader - * message struct and keep it here. Everything that's A/B-specific - * stays after struct bootloader_message, which should be managed by - * the A/B-bootloader or boot control HAL. - * - * The slot_suffix field is used for A/B implementations where the - * bootloader does not set the androidboot.ro.boot.slot_suffix kernel - * commandline parameter. This is used by fs_mgr to mount /system and - * other partitions with the slotselect flag set in fstab. A/B - * implementations are free to use all 32 bytes and may store private - * data past the first NUL-byte in this field. It is encouraged, but - * not mandatory, to use 'struct bootloader_control' described below. - */ -struct bootloader_message_ab { - struct bootloader_message message; - char slot_suffix[32]; - - // Round up the entire struct to 4096-byte. - char reserved[2016]; -}; - -/** - * Be cautious about the struct size change, in case we put anything post - * bootloader_message_ab struct (b/29159185). - */ -/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) -static_assert(sizeof(struct bootloader_message_ab) == 4096, - "struct bootloader_message_ab size changes"); -#endif*/ - -#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */ -#define BOOT_CTRL_VERSION 1 - -struct slot_metadata { - // Slot priority with 15 meaning highest priority, 1 lowest - // priority and 0 the slot is unbootable. - uint8_t priority : 4; - // Number of times left attempting to boot this slot. - uint8_t tries_remaining : 3; - // 1 if this slot has booted successfully, 0 otherwise. - uint8_t successful_boot : 1; - // 1 if this slot is corrupted from a dm-verity corruption, 0 - // otherwise. - uint8_t verity_corrupted : 1; - // Reserved for further use. - uint8_t reserved : 7; -} __attribute__((packed)); - -/* Bootloader Control AB - * - * This struct can be used to manage A/B metadata. It is designed to - * be put in the 'slot_suffix' field of the 'bootloader_message' - * structure described above. It is encouraged to use the - * 'bootloader_control' structure to store the A/B metadata, but not - * mandatory. - */ -struct bootloader_control { - // NUL terminated active slot suffix. - char slot_suffix[4]; - // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC). - uint32_t magic; - // Version of struct being used (see BOOT_CTRL_VERSION). - uint8_t version; - // Number of slots being managed. - uint8_t nb_slot : 3; - // Number of times left attempting to boot recovery. - uint8_t recovery_tries_remaining : 3; - // Ensure 4-bytes alignment for slot_info field. - uint8_t reserved0[2]; - // Per-slot information. Up to 4 slots. - struct slot_metadata slot_info[4]; - // Reserved for further use. - uint8_t reserved1[8]; - // CRC32 of all 28 bytes preceding this field (little endian - // format). - uint32_t crc32_le; -} __attribute__((packed)); - -/*#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus) -static_assert(sizeof(struct bootloader_control) == - sizeof(((struct bootloader_message_ab *)0)->slot_suffix), - "struct bootloader_control has wrong size"); -#endif*/ - -#ifdef __cplusplus - -#include -#include - -// Read bootloader message into boot. Error message will be set in err. -bool read_bootloader_message(bootloader_message* boot, std::string* err); - -// Read bootloader message from the specified misc device into boot. -bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device, - std::string* err); - -// Write bootloader message to BCB. -bool write_bootloader_message(const bootloader_message& boot, std::string* err); - -// Write bootloader message to the specified BCB device. -bool write_bootloader_message_to(const bootloader_message& boot, - const std::string& misc_blk_device, std::string* err); - -// Write bootloader message (boots into recovery with the options) to BCB. Will -// set the command and recovery fields, and reset the rest. -bool write_bootloader_message(const std::vector& options, std::string* err); - -// Update bootloader message (boots into recovery with the options) to BCB. Will -// only update the command and recovery fields. -bool update_bootloader_message(const std::vector& options, std::string* err); - -// Clear BCB. -bool clear_bootloader_message(void* err); -bool clear_bootloader_message(std::string* err); - -// Writes the reboot-bootloader reboot reason to the bootloader_message. -bool write_reboot_bootloader(std::string* err); - -// Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC). -bool read_wipe_package(std::string* package_data, size_t size, std::string* err); - -void set_misc_device(const char* name); -void get_args(int *argc, char ***argv); - -// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC). -bool write_wipe_package(const std::string& package_data, std::string* err); - -#else - -#include - -// C Interface. -bool write_bootloader_message(const char* options); -bool write_reboot_bootloader(void); - -#endif // ifdef __cplusplus - -#endif // _BOOTLOADER_MESSAGE_TWRP_H diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk old mode 100644 new mode 100755 index 0c6ef5b566..8b1372b55f --- a/crypto/ext4crypt/Android.mk +++ b/crypto/ext4crypt/Android.mk @@ -3,12 +3,18 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) include $(CLEAR_VARS) LOCAL_MODULE := libe4crypt -LOCAL_MODULE_TAGS := eng optional +LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS := LOCAL_SRC_FILES := Decrypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp ext4_crypt.cpp LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite 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 hardware/libhardware/include/hardware system/security/softkeymaster/include/keymaster system/keymaster/include +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 ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),) LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX @@ -17,6 +23,9 @@ endif ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) #8.0 or higher LOCAL_CFLAGS += -DHAVE_GATEKEEPER1 + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) + LOCAL_SHARED_LIBRARIES += android.hardware.confirmationui@1.0 + 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) #9.0 rules @@ -66,10 +75,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := twrpfbe LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/ LOCAL_SRC_FILES := main.cpp LOCAL_SHARED_LIBRARIES := libe4crypt -#LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 include $(BUILD_EXECUTABLE) @@ -77,10 +85,10 @@ include $(CLEAR_VARS) LOCAL_MODULE := e4policyget LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/ LOCAL_SRC_FILES := e4policyget.cpp LOCAL_SHARED_LIBRARIES := libe4crypt -LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 +LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64 include $(BUILD_EXECUTABLE) @@ -88,7 +96,7 @@ 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_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/ LOCAL_SRC_FILES := keystore_auth.cpp LOCAL_SHARED_LIBRARIES := libc libkeystore_binder libutils libbinder liblog ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) @@ -96,7 +104,7 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) LOCAL_CFLAGS += -DUSE_SECURITY_NAMESPACE LOCAL_SHARED_LIBRARIES += libkeystore_aidl endif -LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 +LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64 include $(BUILD_EXECUTABLE) diff --git a/crypto/ext4crypt/Decrypt.cpp b/crypto/ext4crypt/Decrypt.cpp old mode 100644 new mode 100755 index d8542dca72..3f82aa17af --- a/crypto/ext4crypt/Decrypt.cpp +++ b/crypto/ext4crypt/Decrypt.cpp @@ -52,10 +52,12 @@ #include #include -#include +#include "ext4_crypt.h" #ifdef USE_KEYSTORAGE_4 -#include +#include +#include +#include #else #include #include @@ -82,6 +84,10 @@ extern "C" { #include +#ifdef USE_KEYSTORAGE_4 +using android::security::keystore::IKeystoreService; +#endif + // Store main DE raw ref / policy extern std::string de_raw_ref; extern std::map s_de_key_raw_refs; @@ -543,15 +549,18 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st std::string disk_decryption_secret_key = ""; 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; - } + // Can be stored in user 0, so check for both. + if (!Find_Keystore_Alias_SubID_And_Prep_Files(user_id, keystore_alias_subid, handle_str) && + !Find_Keystore_Alias_SubID_And_Prep_Files(0, 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 - sp binder = getKeystoreBinderRetry(); + sp binder = getKeystoreBinderRetry(); #ifdef USE_KEYSTORAGE_4 - sp service = interface_cast(binder); + sp service = interface_cast(binder); #else sp service = interface_cast(binder); #endif @@ -653,7 +662,7 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st 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 + * service keystore_auth /system/bin/keystore_auth * disabled * oneshot * user system @@ -798,7 +807,7 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st 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 + * service keystore_auth /system/bin/keystore_auth * disabled * oneshot * user system @@ -1159,15 +1168,15 @@ bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password printf("e4crypt_unlock_user_key returned fail\n"); return Free_Return(retval, weaver_key, &pwd); } -#ifdef USE_KEYSTORAGE_4 +/*#ifdef USE_KEYSTORAGE_4 if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) { #else if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) { #endif printf("failed to e4crypt_prepare_user_storage\n"); return Free_Return(retval, weaver_key, &pwd); - } - printf("Decrypted Successfully!\n"); + }*/ + printf("User %i Decrypted Successfully!\n", user_id); retval = true; return Free_Return(retval, weaver_key, &pwd); } @@ -1194,7 +1203,9 @@ int Get_Password_Type(const userid_t user_id, std::string& filename) { } if (pwd.password_type == 1) // In Android this means pattern return 2; // In TWRP this means pattern - else if (pwd.password_type == 2) // In Android this means PIN or password + // In Android <11 type 2 is PIN or password + // In Android 11 type 3 is PIN and type 4 is password + else if (pwd.password_type > 1) return 1; // In TWRP this means PIN or password return 0; // We'll try the default password #else @@ -1249,15 +1260,15 @@ bool Decrypt_User(const userid_t user_id, const std::string& Password) { printf("e4crypt_unlock_user_key returned fail\n"); return false; } -#ifdef USE_KEYSTORAGE_4 +/*#ifdef USE_KEYSTORAGE_4 if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) { #else if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) { #endif printf("failed to e4crypt_prepare_user_storage\n"); return false; - } - printf("Decrypted Successfully!\n"); + }*/ + printf("User %i Decrypted Successfully!\n", user_id); return true; } if (stat("/data/system_de/0/spblob", &st) == 0) { @@ -1337,14 +1348,14 @@ bool Decrypt_User(const userid_t user_id, const std::string& Password) { printf("e4crypt_unlock_user_key returned fail\n"); return false; } -#ifdef USE_KEYSTORAGE_4 +/*#ifdef USE_KEYSTORAGE_4 if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) { #else if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) { #endif printf("failed to e4crypt_prepare_user_storage\n"); return false; - } - printf("Decrypted Successfully!\n"); + }*/ + printf("User %i Decrypted Successfully!\n", user_id); return true; } diff --git a/crypto/ext4crypt/Ext4Crypt.cpp b/crypto/ext4crypt/Ext4Crypt.cpp index c9e71fd9bb..9360b1a2e1 100644 --- a/crypto/ext4crypt/Ext4Crypt.cpp +++ b/crypto/ext4crypt/Ext4Crypt.cpp @@ -63,6 +63,7 @@ #define MANAGE_MISC_DIRS 0 #include +#include #include //#include @@ -316,16 +317,6 @@ bool lookup_key_ref(const std::map& key_map, userid_t use return true; } -static bool ensure_policy(const std::string& raw_ref __unused, const std::string& path) { - return true; - // ensure policy will set a policy if one is not set on an empty folder - we don't want to do this in recovery - /*if (e4crypt_policy_ensure(path.c_str(), raw_ref.data(), raw_ref.size()) != 0) { - LOG(ERROR) << "Failed to set policy on: " << path << "\n"; - return false; - } - return true;*/ -} - static bool is_numeric(const char* name) { for (const char* p = name; *p != '\0'; p++) { if (!isdigit(*p)) return false; @@ -363,6 +354,9 @@ static bool load_all_de_keys() { if (!install_key(key, &raw_ref)) return false; s_de_key_raw_refs[user_id] = raw_ref; LOG(DEBUG) << "Installed de key for user " << user_id; + + std::string user_prop = "twrp.user." + std::to_string(user_id) + ".decrypt"; + property_set(user_prop.c_str(), "0"); } } // ext4enc:TODO: go through all DE directories, ensure that all user dirs have the @@ -411,13 +405,6 @@ bool e4crypt_init_user0() { // 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 (!e4crypt_prepare_user_storage(nullptr, 0, 0, FLAG_STORAGE_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. @@ -458,79 +445,6 @@ bool e4crypt_unlock_user_key(userid_t user_id, int serial __unused, const char* } } else { printf("Emulation mode not supported in TWRP\n"); - // 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(nullptr, user_id), 0770) || - !emulated_unlock(android::vold::BuildDataUserCePath(nullptr, user_id), 0771)) { - LOG(ERROR) << "Failed to unlock user " << user_id; - return false; - }*/ } return true; } - -bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial __unused, - int flags) { - - if (flags & FLAG_STORAGE_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); - auto foreign_de_path = android::vold::BuildDataProfilesForeignDexDePath(user_id); - - // DE_n key - auto system_de_path = android::vold::BuildDataSystemDePath(user_id); - auto misc_de_path = android::vold::BuildDataMiscDePath(user_id); - auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id); - - 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(foreign_de_path, 0773, 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(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false; - - // For now, FBE is only supported on internal storage - if (e4crypt_is_native() && volume_uuid == nullptr) { - std::string de_raw_ref; - if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_raw_ref)) return false; - if (!ensure_policy(de_raw_ref, system_de_path)) return false; - if (!ensure_policy(de_raw_ref, misc_de_path)) return false; - if (!ensure_policy(de_raw_ref, user_de_path)) return false; - } - } - - if (flags & FLAG_STORAGE_CE) { - // CE_n key - auto system_ce_path = android::vold::BuildDataSystemCePath(user_id); - auto misc_ce_path = android::vold::BuildDataMiscCePath(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 (!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(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; - - // For now, FBE is only supported on internal storage - if (e4crypt_is_native() && volume_uuid == nullptr) { - std::string ce_raw_ref; - if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_raw_ref)) return false; - if (!ensure_policy(ce_raw_ref, system_ce_path)) return false; - if (!ensure_policy(ce_raw_ref, misc_ce_path)) return false; - if (!ensure_policy(ce_raw_ref, media_ce_path)) return false; - if (!ensure_policy(ce_raw_ref, user_ce_path)) return false; - } - } - - return true; -} diff --git a/crypto/ext4crypt/Ext4Crypt.h b/crypto/ext4crypt/Ext4Crypt.h index 57623e35c2..beb5d8fe3d 100644 --- a/crypto/ext4crypt/Ext4Crypt.h +++ b/crypto/ext4crypt/Ext4Crypt.h @@ -38,7 +38,7 @@ bool e4crypt_init_user0(); bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token, const char* secret); //bool e4crypt_lock_user_key(userid_t user_id); -bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial, int flags); +//bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial, int flags); //bool e4crypt_destroy_user_storage(const char* volume_uuid, userid_t user_id, int flags); bool lookup_key_ref(const std::map& key_map, userid_t user_id, diff --git a/crypto/ext4crypt/Ext4CryptPie.cpp b/crypto/ext4crypt/Ext4CryptPie.cpp index 548e4e445c..48b4ec963e 100644 --- a/crypto/ext4crypt/Ext4CryptPie.cpp +++ b/crypto/ext4crypt/Ext4CryptPie.cpp @@ -110,9 +110,9 @@ static bool e4crypt_is_emulated() { return property_get_bool("persist.sys.emulate_fbe", false); } -static const char* escape_empty(const std::string& value) { +/*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); @@ -155,23 +155,6 @@ 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, @@ -259,15 +242,6 @@ static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gi 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) { @@ -327,13 +301,6 @@ static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) { android::base::GetProperty("fbe.filenames", "aes-256-heh"); } -static bool ensure_policy(const PolicyKeyRef& key_ref, const std::string& path) { - return true; - /*return e4crypt_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; @@ -379,6 +346,9 @@ static bool load_all_de_keys() { 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 << std::endl; + + std::string user_prop = "twrp.user." + std::to_string(user_id) + ".decrypt"; + property_set(user_prop.c_str(), "0"); } } // ext4enc:TODO: go through all DE directories, ensure that all user dirs have the @@ -435,13 +405,6 @@ bool e4crypt_init_user0() { // 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 (!e4crypt_prepare_user_storage("", 0, 0, /*android::os::IVold::*/STORAGE_FLAG_DE)) { - LOG(ERROR) << "Failed to prepare user 0 storage" << std::endl; - return false; - } // If this is a non-FBE device that recently left an emulated mode, // restore user data directories to known-good state. @@ -452,92 +415,6 @@ bool e4crypt_init_user0() { return true; } -/*bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) { - LOG(DEBUG) << "TWRP NOT e4crypt_vold_create_user_key for " << user_id << " serial " << serial; - return true; - if (!e4crypt_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 e4crypt_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 (!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) { - LOG(ERROR) << "TWRP NOT evict_ce_key\n"; - return true; - 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 e4crypt_destroy_user_key(userid_t user_id) { - LOG(DEBUG) << "NOT e4crypt_destroy_user_key(" << user_id << ")"; - return true; - if (!e4crypt_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 << std::endl; @@ -566,92 +443,6 @@ static bool parse_hex(const std::string& hex, std::string* result) { 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; - bool wrapped_key_supported = false; - 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 << std::endl; - 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 << std::endl; - return false; - } - android::vold::KeyAuthentication auth("", secdiscardable_hash); - wrapped_key_supported = is_wrapped_key_supported_external(); - if (!android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp", - &key_ref->key_raw_ref, wrapped_key_supported)) - 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 e4crypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token_hex, - const std::string& secret_hex) { - LOG(DEBUG) << "e4crypt_add_user_key_auth " << user_id << " serial=" << serial - << " token_present=" << (token_hex != "!"); - if (!e4crypt_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; - return true; -} - -bool e4crypt_fixate_newest_user_key_auth(userid_t user_id) { - LOG(DEBUG) << "e4crypt_fixate_newest_user_key_auth " << user_id; - if (!e4crypt_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 e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex, const std::string& secret_hex) { @@ -684,224 +475,3 @@ bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& to } return true; } - -// TODO: rename to 'evict' for consistency -/*bool e4crypt_lock_user_key(userid_t user_id) { - LOG(DEBUG) << "TWRP NOTe4crypt_lock_user_key " << user_id; - return true; - if (e4crypt_is_native()) { - return evict_ce_key(user_id); - } else if (e4crypt_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) { - LOG(ERROR) << "not actually forking for vold_prepare_subdirs\n"; - return true; - /*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 e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial, - int flags) { - LOG(DEBUG) << "e4crypt_prepare_user_storage for volume " << escape_empty(volume_uuid) - << ", user " << user_id << ", serial " << serial << ", flags " << flags << std::endl; - - if (flags & /*android::os::IVold::*/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(nullptr, 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 (e4crypt_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, nullptr, &de_ref)) return false; - } - if (!ensure_policy(de_ref, user_de_path)) return false; - } - } - - if (flags & /*android::os::IVold::*/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(nullptr, user_id); - auto user_ce_path = android::vold::BuildDataUserCePath(nullptr, 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 (e4crypt_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, nullptr, &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(misc_ce_path); - } - } - if (!prepare_subdirs("prepare", volume_uuid, user_id, flags)) return false; - return true; -} - -/*bool e4crypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags) { - LOG(DEBUG) << "TWRP NOT e4crypt_destroy_user_storage for volume " << escape_empty(volume_uuid) - << ", user " << user_id << ", flags " << flags; - bool res = true; - return res; - - res &= prepare_subdirs("destroy", volume_uuid, user_id, flags); - - if (flags & android::os::IVold::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(nullptr, user_id); - auto user_ce_path = android::vold::BuildDataUserCePath(nullptr, 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 (e4crypt_is_native()) { - res &= destroy_volkey(misc_ce_path, volume_uuid); - } - } - } - - if (flags & android::os::IVold::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(nullptr, 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 (e4crypt_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) { - LOG(ERROR) << "TWRP NOT destroy_volume_keys\n"; - return true; - 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 e4crypt_destroy_volume_keys(const std::string& volume_uuid) { - bool res = true; - LOG(DEBUG) << "TWRP NOT e4crypt_destroy_volume_keys for volume " << escape_empty(volume_uuid); - /*return res; - 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/ext4crypt/Ext4CryptPie.h b/crypto/ext4crypt/Ext4CryptPie.h index d6d6ecf285..7236bc00f0 100644 --- a/crypto/ext4crypt/Ext4CryptPie.h +++ b/crypto/ext4crypt/Ext4CryptPie.h @@ -32,8 +32,8 @@ bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& to const std::string& secret); //bool e4crypt_lock_user_key(userid_t user_id); -bool e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial, - int flags); +/*bool e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial, + int flags);*/ /*bool e4crypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags); bool e4crypt_destroy_volume_keys(const std::string& volume_uuid);*/ diff --git a/crypto/ext4crypt/ext4_crypt.h b/crypto/ext4crypt/ext4_crypt.h new file mode 100644 index 0000000000..d410ccfd49 --- /dev/null +++ b/crypto/ext4crypt/ext4_crypt.h @@ -0,0 +1,39 @@ +/* + * 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 _EXT4_CRYPT_H_ +#define _EXT4_CRYPT_H_ + +#include +#include +#include + +__BEGIN_DECLS + +bool e4crypt_is_native(); + +int e4crypt_policy_ensure(const char *directory, const char *policy, + size_t policy_length, + const char *contents_encryption_mode, + const char *filenames_encryption_mode); + +static const char* e4crypt_unencrypted_folder = "/unencrypted"; +static const char* e4crypt_key_ref = "/unencrypted/ref"; +static const char* e4crypt_key_mode = "/unencrypted/mode"; + +__END_DECLS + +#endif // _EXT4_CRYPT_H_ diff --git a/crypto/fde/Android.mk b/crypto/fde/Android.mk old mode 100644 new mode 100755 index aafd7a0bce..f78697f75e --- a/crypto/fde/Android.mk +++ b/crypto/fde/Android.mk @@ -3,7 +3,7 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) include $(CLEAR_VARS) LOCAL_MODULE := libcryptfsfde -LOCAL_MODULE_TAGS := eng optional +LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := cryptfs.cpp LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libstdc++ LOCAL_STATIC_LIBRARIES := libscrypttwrp_static @@ -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 @@ -59,7 +65,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := twrpdec LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/ LOCAL_SRC_FILES := main.cpp cryptfs.cpp LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libc libstdc++ LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto @@ -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/fde/cryptfs.cpp b/crypto/fde/cryptfs.cpp index 83522968be..2d1595f387 100644 --- a/crypto/fde/cryptfs.cpp +++ b/crypto/fde/cryptfs.cpp @@ -74,6 +74,7 @@ #include #include #include +#include #endif //#include "android-base/properties.h" //#include @@ -176,7 +177,8 @@ static int keymaster_init(keymaster_device_t **keymaster_dev) } #else //TW_KEYMASTER_MAX_API == 0 static int keymaster_init(keymaster0_device_t **keymaster0_dev, - keymaster1_device_t **keymaster1_dev) + keymaster1_device_t **keymaster1_dev, + keymaster2_device_t **keymaster2_dev) { int rc; @@ -192,7 +194,11 @@ static int keymaster_init(keymaster0_device_t **keymaster0_dev, *keymaster0_dev = NULL; *keymaster1_dev = NULL; - if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) { + *keymaster2_dev = NULL; + if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_2_0) { + printf("Found keymaster2 module, using keymaster2 API.\n"); + rc = keymaster2_open(mod, keymaster2_dev); + } else if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) { printf("Found keymaster1 module, using keymaster1 API.\n"); rc = keymaster1_open(mod, keymaster1_dev); } else { @@ -211,6 +217,7 @@ static int keymaster_init(keymaster0_device_t **keymaster0_dev, err: *keymaster0_dev = NULL; *keymaster1_dev = NULL; + *keymaster2_dev = NULL; return rc; } #endif //TW_KEYMASTER_MAX_API == 0 @@ -424,7 +431,8 @@ static int keymaster_sign_object(struct crypt_mnt_ftr *ftr, #if TW_KEYMASTER_MAX_API >= 1 keymaster0_device_t *keymaster0_dev = 0; keymaster1_device_t *keymaster1_dev = 0; - if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) { + keymaster2_device_t *keymaster2_dev = 0; + if (keymaster_init(&keymaster0_dev, &keymaster1_dev, &keymaster2_dev)) { #else keymaster_device_t *keymaster0_dev = 0; if (keymaster_init(&keymaster0_dev)) { @@ -500,6 +508,82 @@ static int keymaster_sign_object(struct crypt_mnt_ftr *ftr, goto out; } + *signature = (uint8_t*)tmp_sig.data; + *signature_size = tmp_sig.data_length; + rc = 0; + } + else if (keymaster2_dev) { + keymaster_key_blob_t key = { ftr->keymaster_blob, ftr->keymaster_blob_size }; + keymaster_key_param_t params[] = { + keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE), + keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE), + }; + keymaster_key_param_set_t param_set = { params, sizeof(params)/sizeof(*params) }; + keymaster_operation_handle_t op_handle; + keymaster_key_param_t config_params[] = { + // Set these to crazy values so we don't need to synchronize + // the recovery with system updates. + // key upgrades will be required; it will be upgraded in-memory + keymaster_param_int(KM_TAG_OS_VERSION, 999999), + keymaster_param_int(KM_TAG_OS_PATCHLEVEL, 209912), + }; + keymaster_key_param_set_t config_param_set = { config_params, sizeof(config_params)/sizeof(*config_params) }; + keymaster2_dev->configure(keymaster2_dev, &config_param_set); + keymaster_error_t error = keymaster2_dev->begin(keymaster2_dev, KM_PURPOSE_SIGN, &key, + ¶m_set, NULL /* out_params */, + &op_handle); + if (error == KM_ERROR_KEY_RATE_LIMIT_EXCEEDED) { + // Key usage has been rate-limited. Wait a bit and try again. + sleep(KEYMASTER_CRYPTFS_RATE_LIMIT); + error = keymaster2_dev->begin(keymaster2_dev, KM_PURPOSE_SIGN, &key, + ¶m_set, NULL /* out_params */, + &op_handle); + } + + if (error == KM_ERROR_KEY_REQUIRES_UPGRADE) { + // Upgrade key in-memory if required + // Do not actually write it back; just keep it in memory + const keymaster_key_blob_t key_to_upd = key; + keymaster2_dev->upgrade_key(keymaster2_dev, &key_to_upd, &config_param_set, &key); + error = keymaster2_dev->begin(keymaster2_dev, KM_PURPOSE_SIGN, &key, + ¶m_set, NULL /* out_params */, + &op_handle); + } + + if (error != KM_ERROR_OK) { + printf("Error starting keymaster signature transaction: %d\n", error); + rc = -1; + goto out; + } + + keymaster_blob_t input = { to_sign, to_sign_size }; + size_t input_consumed; + error = keymaster2_dev->update(keymaster2_dev, op_handle, NULL /* in_params */, + &input, &input_consumed, NULL /* out_params */, + NULL /* output */); + if (error != KM_ERROR_OK) { + printf("Error sending data to keymaster signature transaction: %d\n", error); + rc = -1; + goto out; + } + if (input_consumed != to_sign_size) { + // This should never happen. If it does, it's a bug in the keymaster implementation. + printf("Keymaster update() did not consume all data.\n"); + keymaster2_dev->abort(keymaster2_dev, op_handle); + rc = -1; + goto out; + } + + keymaster_blob_t tmp_sig; + error = keymaster2_dev->finish(keymaster2_dev, op_handle, NULL /* in_params */, + NULL, NULL /* verify signature */, NULL /* out_params */, + &tmp_sig); + if (error != KM_ERROR_OK) { + printf("Error finishing keymaster signature transaction: %d\n", error); + rc = -1; + goto out; + } + *signature = (uint8_t*)tmp_sig.data; *signature_size = tmp_sig.data_length; rc = 0; @@ -528,7 +612,9 @@ static int keymaster_sign_object(struct crypt_mnt_ftr *ftr, ftr->keymaster_blob, KEYMASTER_BLOB_SIZE, &ftr->keymaster_blob_size); #endif //TW_KEYMASTER_MAX_API == 3 #if TW_KEYMASTER_MAX_API >= 4 - //for (;;) { + for (int c = 1;c <= 20;c++) { // 20 tries are enough for signing keymaster + if (c > 2) + usleep(5000); // if failed in two tries lets rest auto result = keymaster_sign_object_for_cryptfs_scrypt( ftr->keymaster_blob, ftr->keymaster_blob_size, KEYMASTER_CRYPTFS_RATE_LIMIT, to_sign, to_sign_size, signature, signature_size); @@ -552,8 +638,7 @@ static int keymaster_sign_object(struct crypt_mnt_ftr *ftr, SLOGE("Failed to write upgraded key to disk"); }*/ SLOGD("Key upgraded successfully\n"); - return 0; - //} + } #endif return -1; } @@ -912,7 +997,10 @@ static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr, tgt->length = crypt_ftr->fs_size; crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); buff_offset = crypt_params - buffer; - SLOGI("Extra parameters for dm_crypt: %s\n", extra_params); + SLOGI( + "Creating crypto dev \"%s\"; cipher=%s, keysize=%u, real_dev=%s, len=%llu, params=\"%s\"\n", + name, crypt_ftr->crypto_type_name, crypt_ftr->keysize, real_blk_name, tgt->length * 512, + extra_params); #ifdef CONFIG_HW_DISK_ENCRYPTION if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) { @@ -945,7 +1033,9 @@ static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr, tgt->next = crypt_params - buffer; for (i = 0; i < TABLE_LOAD_RETRIES; i++) { - if (! ioctl(fd, DM_TABLE_LOAD, io)) { + int ret = ioctl(fd, DM_TABLE_LOAD, io); + if (!ret) { + SLOGI("ioctl err: %d", ret); break; } usleep(500000); diff --git a/crypto/fscrypt/Android.mk b/crypto/fscrypt/Android.mk new file mode 100755 index 0000000000..0fa36c8a3b --- /dev/null +++ b/crypto/fscrypt/Android.mk @@ -0,0 +1,80 @@ +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)/system/bin +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)/system/bin +LOCAL_SRC_FILES := fscryptpolicyget.cpp +LOCAL_SHARED_LIBRARIES := libtwrpfscrypt + +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)/system/bin +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 + +include $(BUILD_EXECUTABLE) + +endif diff --git a/crypto/fscrypt/Checkpoint.h b/crypto/fscrypt/Checkpoint.h new file mode 100644 index 0000000000..63ead837b7 --- /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 0000000000..80ad40736a --- /dev/null +++ b/crypto/fscrypt/Decrypt.cpp @@ -0,0 +1,1174 @@ +/* + * 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; + // Can be stored in user 0, so check for both. + if (!Find_Keystore_Alias_SubID_And_Prep_Files(user_id, keystore_alias_subid, handle_str) && + !Find_Keystore_Alias_SubID_And_Prep_Files(0, 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 /system/bin/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("User %i Decrypted Successfully!\n", user_id); + 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 + } + // In Android <11 type 2 is PIN or password + // In Android 11 type 3 is PIN and type 4 is password + else if (pwd.password_type > 1) { + 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("User %i Decrypted Successfully!\n", user_id); + 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 0000000000..3755718241 --- /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/otafault/test.cpp b/crypto/fscrypt/EncryptInplace.h similarity index 53% rename from otafault/test.cpp rename to crypto/fscrypt/EncryptInplace.h index 63e2445af6..bf0c3140f9 100644 --- a/otafault/test.cpp +++ b/crypto/fscrypt/EncryptInplace.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -14,22 +14,18 @@ * limitations under the License. */ -#include -#include -#include +#ifndef _ENCRYPT_INPLACE_H +#define _ENCRYPT_INPLACE_H + #include -#include -#include "otafault/ota_io.h" +#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); -int main(int /* argc */, char** /* argv */) { - int fd = open("testdata/test.file", O_RDWR); - char buf[8]; - const char* out = "321"; - int readv = ota_read(fd, buf, 4); - printf("Read returned %d\n", readv); - int writev = ota_write(fd, out, 4); - printf("Write returned %d\n", writev); - close(fd); - return 0; -} +#endif diff --git a/crypto/fscrypt/FsCrypt.cpp b/crypto/fscrypt/FsCrypt.cpp new file mode 100755 index 0000000000..ade9fe83cd --- /dev/null +++ b/crypto/fscrypt/FsCrypt.cpp @@ -0,0 +1,1013 @@ +/* + * 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 "Keymaster.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::Keymaster; +using android::hardware::keymaster::V4_0::KeyFormat; +// 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 = "/system/bin/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 is_wrapped_key_supported_common(const std::string& mount_point) { + LOG(DEBUG) << "Determining wrapped-key support for " << mount_point; + std::string wrapped_key_supported = android::base::GetProperty("fbe.data.wrappedkey", "false"); + LOG(DEBUG) << "fbe.data.wrappedkey = " << wrapped_key_supported; + if (mount_point == DATA_MNT_POINT && wrapped_key_supported == "true") { + LOG(DEBUG) << "Wrapped key supported on " << mount_point; + return true; + } else { + return false; + } +} + +bool is_wrapped_key_supported() { + return is_wrapped_key_supported_common(DATA_MNT_POINT); +} + +bool is_wrapped_key_supported_external() { + return false; +} + +bool is_metadata_wrapped_key_supported_common(const std::string& mount_point) { + LOG(DEBUG) << "Determining metadata wrapped-key support for " << mount_point; + std::string wrapped_key_supported = android::base::GetProperty("fbe.metadata.wrappedkey", "false"); + LOG(DEBUG) << "fbe.metadata.wrappedkey = " << wrapped_key_supported; + if (mount_point == METADATA_MNT_POINT && wrapped_key_supported == "true") { + LOG(DEBUG) << "Wrapped key supported on " << mount_point; + return true; + } else { + return false; + } +} + +bool is_metadata_wrapped_key_supported() { + return is_metadata_wrapped_key_supported_common(METADATA_MNT_POINT); +} + +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 (is_wrapped_key_supported()) { + KeyBuffer ephemeral_wrapped_key; + if (!getEphemeralWrappedKey(KeyFormat::RAW, ce_key, &ephemeral_wrapped_key)) { + LOG(ERROR) << "Failed to export ce key"; + return false; + } + ce_key = std::move(ephemeral_wrapped_key); + } + 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(is_wrapped_key_supported()) { + if (!generateWrappedKey(user_id, android::vold::KeyType::DE_USER, &de_key)) return false; + if (!generateWrappedKey(user_id, android::vold::KeyType::CE_USER, &ce_key)) return false; + } else { + 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; + } + + std::string ce_raw_ref; + if (is_wrapped_key_supported()) { + KeyBuffer ephemeral_wrapped_de_key; + KeyBuffer ephemeral_wrapped_ce_key; + + /* Export and install the DE keys */ + if (!getEphemeralWrappedKey(KeyFormat::RAW, de_key, &ephemeral_wrapped_de_key)) { + LOG(ERROR) << "Failed to export de_key"; + return false; + } + /* Export and install the CE keys */ + if (!getEphemeralWrappedKey(KeyFormat::RAW, ce_key, &ephemeral_wrapped_ce_key)) { + LOG(ERROR) << "Failed to export de_key"; + return false; + } + + de_key = std::move(ephemeral_wrapped_de_key); + ce_key = std::move(ephemeral_wrapped_ce_key); + } + if (!android::vold::installKey(de_key, &de_raw_ref)) return false; + if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false; + s_ce_keys[user_id] = std::move(ce_key); + + s_de_key_raw_refs[user_id] = de_raw_ref; + 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; + }*/ + LOG(INFO) << "contents mode '" << android::base::GetProperty("fbe.contents", "aes-256-xts") << "' filenames '" << android::base::GetProperty("fbe.filenames", "aes-256-heh") << "'\n"; + key_ref->contents_mode = + android::base::GetProperty("fbe.contents", "aes-256-xts"); + key_ref->filenames_mode = + android::base::GetProperty("fbe.filenames", "aes-256-heh"); +} + +static bool ensure_policy(const PolicyKeyRef& key_ref, const std::string& path) { + return true; + /*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 (is_wrapped_key_supported()) { + KeyBuffer ephemeral_wrapped_key; + if (!getEphemeralWrappedKey(KeyFormat::RAW, key, &ephemeral_wrapped_key)) { + LOG(ERROR) << "Failed to export de_key in create_and_install_user_keys"; + return false; + } + key = std::move(ephemeral_wrapped_key); + } + 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; + + std::string user_prop = "twrp.user." + std::to_string(user_id) + ".decrypt"; + property_set(user_prop.c_str(), "0"); + } + } + // 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"; + bool wrapped_key_supported = false; + + if (s_systemwide_keys_initialized) { + LOG(INFO) << "Already initialized"; + return true; + } + + PolicyKeyRef device_ref; + wrapped_key_supported = is_wrapped_key_supported(); + + if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, + device_key_path, device_key_temp, + &device_ref.key_raw_ref, wrapped_key_supported)) + 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; + bool wrapped_key_supported = false; + 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); + wrapped_key_supported = is_wrapped_key_supported_external(); + + if (!android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp", + &key_ref->key_raw_ref, wrapped_key_supported)) + 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 const directory_path = get_ce_key_directory_path(user_id); + auto const paths = get_ce_key_paths(directory_path); + + KeyBuffer ce_key; + if(is_wrapped_key_supported()) { + std::string ce_key_current_path = get_ce_key_current_path(directory_path); + if (android::vold::retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key)) { + LOG(DEBUG) << "Successfully retrieved key"; + } else { + if (android::vold::retrieveKey(ce_key_current_path, auth, &ce_key)) { + LOG(DEBUG) << "Successfully retrieved key"; + } + } + } else { + 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; + } + ce_key = it->second; + } + + 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_clear_user_key_auth(userid_t user_id, int serial, const std::string& token_hex, + const std::string& secret_hex) { + LOG(DEBUG) << "fscrypt_clear_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; + + if (is_wrapped_key_supported()) { + auto const directory_path = get_ce_key_directory_path(user_id); + auto const paths = get_ce_key_paths(directory_path); + + KeyBuffer ce_key; + std::string ce_key_current_path = get_ce_key_current_path(directory_path); + + auto auth = android::vold::KeyAuthentication(token, secret); + /* Retrieve key while removing a pin. A secret is needed */ + if (android::vold::retrieveKey(ce_key_current_path, auth, &ce_key)) { + LOG(DEBUG) << "Successfully retrieved key"; + } else { + /* Retrieve key when going None to swipe and vice versa when a + synthetic password is present */ + if (android::vold::retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key)) { + LOG(DEBUG) << "Successfully retrieved key"; + } + } + + 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; + } else { + if(!fscrypt_add_user_key_auth(user_id, serial, "!", "!")) 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 0000000000..c63e11f0af --- /dev/null +++ b/crypto/fscrypt/FsCrypt.h @@ -0,0 +1,47 @@ +/* + * 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_clear_user_key_auth(userid_t user_id, int serial, const std::string& token_hex, + const std::string& secret_hex); +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 is_wrapped_key_supported(); +bool is_wrapped_key_supported_external(); +bool is_metadata_wrapped_key_supported(); +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 0000000000..07ecb1f7d7 --- /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 0000000000..73880b1ba9 --- /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/mtp/legacy/MtpUtils.h b/crypto/fscrypt/KeyBuffer.cpp similarity index 53% rename from mtp/legacy/MtpUtils.h rename to crypto/fscrypt/KeyBuffer.cpp index 2bca94b2ef..e7aede5329 100644 --- a/mtp/legacy/MtpUtils.h +++ b/crypto/fscrypt/KeyBuffer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * 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. @@ -12,16 +12,26 @@ * 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. - * - * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++ */ -#ifndef _MTP_UTILS_H -#define _MTP_UTILS_H +#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); +} -#include +KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs) { + std::copy(rhs, rhs + strlen(rhs), std::back_inserter(lhs)); + return std::move(lhs); +} -bool parseDateTime(const char* dateTime, time_t& outSeconds); -void formatDateTime(time_t seconds, char* buffer, int bufferLength); +} // namespace vold +} // namespace android -#endif // _MTP_UTILS_H diff --git a/crypto/fscrypt/KeyBuffer.h b/crypto/fscrypt/KeyBuffer.h new file mode 100644 index 0000000000..2087187df6 --- /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 0000000000..068c0693ec --- /dev/null +++ b/crypto/fscrypt/KeyStorage.cpp @@ -0,0 +1,658 @@ +/* + * 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 +constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64; + +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); +} + +bool generateWrappedKey(userid_t user_id, KeyType key_type, + KeyBuffer* key) { + Keymaster keymaster; + if (!keymaster) return false; + *key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE); + std::string key_temp; + auto paramBuilder = km::AuthorizationSetBuilder() + .AesEncryptionKey(AES_KEY_BYTES * 8) + .GcmModeMinMacLen(GCM_MAC_BYTES * 8) + .Authorization(km::TAG_USER_ID, user_id); + km::KeyParameter param1; + param1.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_FBE_ICE); + param1.f.boolValue = true; + paramBuilder.push_back(param1); + + km::KeyParameter param2; + if ((key_type == KeyType::DE_USER) || (key_type == KeyType::DE_SYS || (key_type == KeyType::ME))) { + param2.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_KEY_TYPE); + param2.f.integer = 0; + } else if (key_type == KeyType::CE_USER) { + param2.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_KEY_TYPE); + param2.f.integer = 1; + } + paramBuilder.push_back(param2); + + if (!keymaster.generateKey(paramBuilder, &key_temp)) return false; + *key = KeyBuffer(key_temp.size()); + memcpy(reinterpret_cast(key->data()), key_temp.c_str(), key->size()); + return true; +} + +bool getEphemeralWrappedKey(km::KeyFormat format, KeyBuffer& kmKey, KeyBuffer* key) { + std::string key_temp; + Keymaster keymaster; + if (!keymaster) return false; + + //Export once, if upgrade needed, upgrade and export again + bool export_again = true; + while (export_again) { + export_again = false; + auto ret = keymaster.exportKey(format, kmKey, "!", "!", &key_temp); + if (ret == km::ErrorCode::OK) { + *key = KeyBuffer(key_temp.size()); + memcpy(reinterpret_cast(key->data()), key_temp.c_str(), key->size()); + return true; + } + if (ret != km::ErrorCode::KEY_REQUIRES_UPGRADE) return false; + LOG(DEBUG) << "Upgrading key"; + std::string kmKeyStr(reinterpret_cast(kmKey.data()), kmKey.size()); + std::string newKey; + if (!keymaster.upgradeKey(kmKeyStr, km::AuthorizationSet(), &newKey)) return false; + memcpy(reinterpret_cast(kmKey.data()), newKey.c_str(), kmKey.size()); + LOG(INFO) << "Key upgraded"; + export_again = true; + } + //Should never come here + return false; +} + +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 0000000000..9959ce65f1 --- /dev/null +++ b/crypto/fscrypt/KeyStorage.h @@ -0,0 +1,84 @@ +/* + * 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 "Keymaster.h" +#include "KeyBuffer.h" +#include +#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; +}; + +enum class KeyType { + DE_SYS, + DE_USER, + CE_USER, + ME, +}; + +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); +bool generateWrappedKey(userid_t user_id, KeyType key_type, KeyBuffer* key); +bool getEphemeralWrappedKey(km::KeyFormat format, KeyBuffer& kmKey, KeyBuffer* key); +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/fscrypt/KeyUtil.cpp b/crypto/fscrypt/KeyUtil.cpp new file mode 100755 index 0000000000..fa40640ed5 --- /dev/null +++ b/crypto/fscrypt/KeyUtil.cpp @@ -0,0 +1,237 @@ +/* + * 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 "FsCrypt.h" +#include "KeyStorage.h" +#include "Utils.h" + +#define MAX_USER_ID 0xFFFFFFFF + +using android::hardware::keymaster::V4_0::KeyFormat; +using android::vold::KeyType; +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; + if (is_wrapped_key_supported()) { + /* When wrapped key is supported, only the first 32 bytes are + the same per boot. The second 32 bytes can change as the ephemeral + key is different. */ + *raw_ref = generateKeyRef(fs_key.raw, (fs_key.size)/2); + } else { + *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, bool wrapped_key_supported) { + 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 (wrapped_key_supported) { + if(!generateWrappedKey(MAX_USER_ID, KeyType::DE_SYS, &key)) return false; + } else { + if (!randomKey(&key)) return false; + } + if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false; + } + + if (wrapped_key_supported) { + KeyBuffer ephemeral_wrapped_key; + if (!getEphemeralWrappedKey(KeyFormat::RAW, key, &ephemeral_wrapped_key)) { + LOG(ERROR) << "Failed to export key in retrieveAndInstallKey"; + return false; + } + key = std::move(ephemeral_wrapped_key); + } + + 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; + if (is_metadata_wrapped_key_supported()) { + KeyBuffer ephemeral_wrapped_key; + if (!getEphemeralWrappedKey(KeyFormat::RAW, *key, &ephemeral_wrapped_key)) { + LOG(ERROR) << "Failed to export key for retrieved key"; + return false; + } + *key = std::move(ephemeral_wrapped_key); + } + } else { + if (!create_if_absent) { + LOG(ERROR) << "No key found in " << key_path; + return false; + } + LOG(ERROR) << "Creating new key in " << key_path; + if (is_metadata_wrapped_key_supported()) { + if(!generateWrappedKey(MAX_USER_ID, KeyType::ME, key)) return false; + } else { + if (!randomKey(key)) return false; + } + LOG(ERROR) << "retrieveKey1"; + if (!storeKeyAtomically(key_path, tmp_path, + kEmptyAuthentication, *key)) return false; + if (is_metadata_wrapped_key_supported()) { + KeyBuffer ephemeral_wrapped_key; + if (!getEphemeralWrappedKey(KeyFormat::RAW, *key, &ephemeral_wrapped_key)) { + LOG(ERROR) << "Failed to export key for generated key"; + return false; + } + *key = std::move(ephemeral_wrapped_key); + } + } + return true; +} + +} // namespace vold +} // namespace android diff --git a/crypto/fscrypt/KeyUtil.h b/crypto/fscrypt/KeyUtil.h new file mode 100755 index 0000000000..2839b4a015 --- /dev/null +++ b/crypto/fscrypt/KeyUtil.h @@ -0,0 +1,42 @@ +/* + * 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 "Keymaster.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 wrapped_key_supported); +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 0000000000..706181dfe4 --- /dev/null +++ b/crypto/fscrypt/Keymaster.cpp @@ -0,0 +1,372 @@ +/* + * 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; +} + +km::ErrorCode Keymaster::exportKey(km::KeyFormat format, KeyBuffer& kmKey, const std::string& clientId, + const std::string& appData, std::string* key) { + auto kmKeyBlob = km::support::blob2hidlVec(std::string(kmKey.data(), kmKey.size())); + auto emptyAssign = NULL; + auto kmClientId = (clientId == "!") ? emptyAssign: km::support::blob2hidlVec(clientId); + auto kmAppData = (appData == "!") ? emptyAssign: km::support::blob2hidlVec(appData); + km::ErrorCode km_error; + auto hidlCb = [&](km::ErrorCode ret, const hidl_vec& exportedKeyBlob) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + if(key) + key->assign(reinterpret_cast(&exportedKeyBlob[0]), + exportedKeyBlob.size()); + }; + auto error = mDevice->exportKey(format, kmKeyBlob, kmClientId, kmAppData, hidlCb); + if (!error.isOk()) { + LOG(ERROR) << "export_key failed: " << error.description(); + return km::ErrorCode::UNKNOWN_ERROR; + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "export_key failed, code " << int32_t(km_error); + return km_error; + } + return km::ErrorCode::OK; +} + +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 0000000000..7d4d9c93ae --- /dev/null +++ b/crypto/fscrypt/Keymaster.h @@ -0,0 +1,162 @@ +/* + * 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); + // Export a key from keymaster. + km::ErrorCode exportKey(km::KeyFormat format, KeyBuffer& kmKey, const std::string& clientId, + const std::string& appData, 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 0000000000..45f3af32e0 --- /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 0000000000..cd0f5e57e0 --- /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 0000000000..3d8e3d746c --- /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 0000000000..1406782083 --- /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 0000000000..669809b9ff --- /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 0000000000..1b43ea5741 --- /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 0000000000..aa71d8fbea --- /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 0000000000..af4e401b51 --- /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/minadbd/minadbd.h b/crypto/fscrypt/VoldUtil.cpp similarity index 82% rename from minadbd/minadbd.h rename to crypto/fscrypt/VoldUtil.cpp index 3570a5da5c..082f7434e5 100644 --- a/minadbd/minadbd.h +++ b/crypto/fscrypt/VoldUtil.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * 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. @@ -14,9 +14,6 @@ * limitations under the License. */ -#ifndef MINADBD_H__ -#define MINADBD_H__ +#include "VoldUtil.h" -int minadbd_main(); - -#endif +android::fs_mgr::Fstab fstab_default; diff --git a/adb_install.h b/crypto/fscrypt/VoldUtil.h similarity index 72% rename from adb_install.h rename to crypto/fscrypt/VoldUtil.h index 97dc83d813..173c598695 100644 --- a/adb_install.h +++ b/crypto/fscrypt/VoldUtil.h @@ -14,15 +14,11 @@ * limitations under the License. */ -#ifndef _ADB_INSTALL_H -#define _ADB_INSTALL_H +#pragma once -#include +#include +#include -//class RecoveryUI; +extern android::fs_mgr::Fstab fstab_default; -//static void set_usb_driver(bool enabled); -//static void maybe_restart_adbd(); -int apply_from_adb(const char* install_file, pid_t* child_pid); - -#endif +#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 0000000000..ea357edcca --- /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 0000000000..22f401e701 --- /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 0000000000..a4fe87bb52 --- /dev/null +++ b/crypto/fscrypt/cryptfs.h @@ -0,0 +1,262 @@ +/* + * 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" +#define METADATA_MNT_POINT "/metadata" + +/* 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 0000000000..43d955227e --- /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 0000000000..01fc41901e --- /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 0000000000..5add616389 --- /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 0000000000..a65fe21bc7 --- /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 0000000000..f0266ae106 --- /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/fuse_sdcard_provider.h b/crypto/fscrypt/sehandle.h similarity index 85% rename from fuse_sdcard_provider.h rename to crypto/fscrypt/sehandle.h index bdc60f2ba2..8921db5b1e 100644 --- a/fuse_sdcard_provider.h +++ b/crypto/fscrypt/sehandle.h @@ -14,9 +14,11 @@ * limitations under the License. */ -#ifndef __FUSE_SDCARD_PROVIDER_H -#define __FUSE_SDCARD_PROVIDER_H +#ifndef _SEHANDLE_H +#define _SEHANDLE_H -bool start_sdcard_fuse(const char* path); +#include + +extern struct selabel_handle* sehandle; #endif diff --git a/crypto/vold_decrypt/Android.mk b/crypto/vold_decrypt/Android.mk old mode 100644 new mode 100755 index 860e61f203..a36abf4932 --- a/crypto/vold_decrypt/Android.mk +++ b/crypto/vold_decrypt/Android.mk @@ -17,7 +17,6 @@ LOCAL_PATH := $(call my-dir) ifeq ($(TW_INCLUDE_CRYPTO), true) ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),) - # Parse TW_CRYPTO_USE_SYSTEM_VOLD ifeq ($(TW_CRYPTO_USE_SYSTEM_VOLD),true) # Just enabled, so only vold + vdc @@ -39,7 +38,7 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) include $(CLEAR_VARS) LOCAL_MODULE := init.recovery.vold_decrypt.rc - LOCAL_MODULE_TAGS := eng + LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES # Cannot send to TARGET_RECOVERY_ROOT_OUT since build system wipes init*.rc @@ -75,7 +74,7 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) include $(CLEAR_VARS) LOCAL_MODULE := libvolddecrypt - LOCAL_MODULE_TAGS := eng optional + LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS := -Wall ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) LOCAL_C_INCLUDES += external/stlport/stlport bionic bionic/libstdc++/include @@ -107,17 +106,29 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) endif endif + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) + ifeq ($(TW_INCLUDE_LIBRESETPROP), true) + LOCAL_CFLAGS += -DTW_INCLUDE_LIBRESETPROP + endif + endif + LOCAL_SRC_FILES = vold_decrypt.cpp LOCAL_SHARED_LIBRARIES := libcutils + LOCAL_C_INCLUDES += system/extras/ext4_utils/include + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0) + LOCAL_C_INCLUDES += bootable/recovery/crypto/fscrypt + else + LOCAL_C_INCLUDES += bootable/recovery/crypto/ext4crypt + endif include $(BUILD_STATIC_LIBRARY) ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) include $(CLEAR_VARS) LOCAL_MODULE := vdc_pie LOCAL_SRC_FILES := vdc_pie.cpp - LOCAL_MODULE_TAGS := eng + LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES - LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin + LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/ LOCAL_CLANG := true LOCAL_TIDY := true LOCAL_TIDY_FLAGS := -warnings-as-errors=clang-analyzer-security*,cert-* @@ -130,12 +141,12 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) endif ifneq ($(TARGET_ARCH), arm64) ifneq ($(TARGET_ARCH), x86_64) - LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker + LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker else - LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64 endif else - LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64 endif include $(BUILD_EXECUTABLE) diff --git a/crypto/vold_decrypt/vold_decrypt.cpp b/crypto/vold_decrypt/vold_decrypt.cpp old mode 100644 new mode 100755 index ac872ea280..4a76405d5a --- a/crypto/vold_decrypt/vold_decrypt.cpp +++ b/crypto/vold_decrypt/vold_decrypt.cpp @@ -81,7 +81,7 @@ int sdkver = 20; #ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG #ifndef VD_STRACE_BIN -#define VD_STRACE_BIN "/sbin/strace" +#define VD_STRACE_BIN "/system/bin/strace" #endif bool has_strace = false; @@ -399,7 +399,7 @@ void Symlink_Firmware_Files(bool is_vendor_symlinked, bool is_firmware_symlinked LOGINFO("%d file(s) symlinked.\n", (int)FirmwareFiles.size()); } -// Android 8.0 fs_mgr checks for "/sbin/recovery", in which case it will +// Android 8.0 fs_mgr checks for "/system/bin/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) { @@ -716,8 +716,8 @@ vector Get_List_Of_Additional_Services(void) { 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 if (Service_Exists("system/bin" + services[j].Service_Name)) + services[j].TWRP_Service_Name = "system/bin" + services[j].Service_Name; else services[j].TWRP_Service_Name.clear(); @@ -780,6 +780,128 @@ void Set_Needed_Properties(void) { property_set("vendor.sys.listeners.registered", "false"); } +void Update_Patch_Level(void) { + // On Oreo and above, keymaster requires Android version & patch level to match installed system + string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk"); + if (!sdkverstr.empty()) { + sdkver = atoi(sdkverstr.c_str()); + } + if (sdkver <= 25) { + property_set("vold_decrypt.legacy_system", "true"); + } else { + LOGINFO("Current system is Oreo or above. Setting OS version and security patch level from installed system...\n"); + property_set("vold_decrypt.legacy_system", "false"); + } + + char prop_value[PROPERTY_VALUE_MAX]; + char legacy_system_value[PROPERTY_VALUE_MAX] = "false"; + property_get("vold_decrypt.legacy_system", prop_value, ""); + + // Only set OS ver and patch level if device uses Oreo+ system + if (strcmp(prop_value, legacy_system_value) == 0) { + property_get("ro.build.version.release", prop_value, ""); + std::string osver_orig = prop_value; + property_set("vold_decrypt.osver_orig", osver_orig.c_str()); + LOGINFO("Current OS version: %s\n", osver_orig.c_str()); + + int error = 0; + std::string osver = TWFunc::System_Property_Get("ro.build.version.release"); + if (!(osver == osver_orig)) { + if (!(error = TWFunc::Property_Override("ro.build.version.release", osver))) { + LOGINFO("Property override successful! New OS version: %s\n", osver.c_str()); + } else { + LOGERROR("Property override failed, code %d\n", error); + return; + } + // TODO: Confirm whether we actually need to update the props in prop.default + std::string sed_osver = "sed -i 's/ro.build.version.release=.*/ro.build.version.release=" + osver + "/g' /prop.default"; + TWFunc::Exec_Cmd(sed_osver); + property_set("vold_decrypt.osver_set", "true"); + } else { + LOGINFO("Current OS version & System OS version already match. Proceeding to next step.\n"); + property_set("vold_decrypt.osver_set", "false"); + } + + property_get("ro.build.version.security_patch", prop_value, ""); + std::string patchlevel_orig = prop_value; + property_set("vold_decrypt.patchlevel_orig", patchlevel_orig.c_str()); + LOGINFO("Current security patch level: %s\n", patchlevel_orig.c_str()); + + std::string patchlevel = TWFunc::System_Property_Get("ro.build.version.security_patch"); + if (!(patchlevel == patchlevel_orig)) { + if (!(error = TWFunc::Property_Override("ro.build.version.security_patch", patchlevel))) { + LOGINFO("Property override successful! New security patch level: %s\n", patchlevel.c_str()); + } else { + LOGERROR("Property override failed, code %d\n", error); + return; + } + // TODO: Confirm whether we actually need to update the props in prop.default + std::string sed_patchlevel = "sed -i 's/ro.build.version.security_patch=.*/ro.build.version.security_patch=" + patchlevel + "/g' /prop.default"; + TWFunc::Exec_Cmd(sed_patchlevel); + property_set("vold_decrypt.patchlevel_set", "true"); + } else { + LOGINFO("Current security patch level & System security patch level already match. Proceeding to next step.\n"); + property_set("vold_decrypt.patchlevel_set", "false"); + } + return; + } else { + LOGINFO("Current system is Nougat or older. Skipping OS version and security patch level setting...\n"); + return; + } +} + +void Revert_Patch_Level(void) { + char osver_set[PROPERTY_VALUE_MAX]; + char patchlevel_set[PROPERTY_VALUE_MAX]; + char osver_patchlevel_set[PROPERTY_VALUE_MAX] = "false"; + + property_get("vold_decrypt.osver_set", osver_set, ""); + property_get("vold_decrypt.patchlevel_set", patchlevel_set, ""); + + int osver_result = strcmp(osver_set, osver_patchlevel_set); + int patchlevel_result = strcmp(patchlevel_set, osver_patchlevel_set); + if (!(osver_result == 0 && patchlevel_result == 0)) { + char prop_value[PROPERTY_VALUE_MAX]; + LOGINFO("Reverting OS version and security patch level to original TWRP values...\n"); + property_get("vold_decrypt.osver_orig", prop_value, ""); + std::string osver_orig = prop_value; + property_get("ro.build.version.release", prop_value, ""); + std::string osver = prop_value; + + int error = 0; + if (!(osver == osver_orig)) { + if (!(error = TWFunc::Property_Override("ro.build.version.release", osver_orig))) { + LOGINFO("Property override successful! Original OS version: %s\n", osver_orig.c_str()); + } else { + LOGERROR("Property override failed, code %d\n", error); + return; + } + // TODO: Confirm whether we actually need to update the props in prop.default + std::string sed_osver_orig = "sed -i 's/ro.build.version.release=.*/ro.build.version.release=" + osver_orig + "/g' /prop.default"; + TWFunc::Exec_Cmd(sed_osver_orig); + } + + property_get("vold_decrypt.patchlevel_orig", prop_value, ""); + std::string patchlevel_orig = prop_value; + property_get("ro.build.version.security_patch", prop_value, ""); + std::string patchlevel = prop_value; + + if (!(patchlevel == patchlevel_orig)) { + if (!(error = TWFunc::Property_Override("ro.build.version.security_patch", patchlevel_orig))) { + LOGINFO("Property override successful! Original security patch level: %s\n", patchlevel_orig.c_str()); + } else { + LOGERROR("Property override failed, code %d\n", error); + return; + } + // TODO: Confirm whether we actually need to update the props in prop.default + std::string sed_patchlevel_orig = "sed -i 's/ro.build.version.security_patch=.*/ro.build.version.security_patch=" + patchlevel_orig + "/g' /prop.default"; + TWFunc::Exec_Cmd(sed_patchlevel_orig); + } + } else { + return; + } +} + static unsigned int get_blkdev_size(int fd) { unsigned long nr_sec; @@ -873,7 +995,7 @@ int Exec_vdc_cryptfs(const string& command, const string& argument, vdc_ReturnVa } // getpwtype and checkpw commands are removed from Pie vdc, using modified vdc_pie - const char *cmd[] = { "/sbin/vdc_pie", "cryptfs" }; + const char *cmd[] = { "/system/bin/vdc_pie", "cryptfs" }; if (sdkver < 28) cmd[0] = "/system/bin/vdc"; const char *env[] = { "LD_LIBRARY_PATH=/system/lib64:/system/lib", NULL }; @@ -1161,6 +1283,9 @@ int Vold_Decrypt_Core(const string& Password) { Symlink_Firmware_Files(is_vendor_symlinked, is_firmware_symlinked); Set_Needed_Properties(); +#ifdef TW_INCLUDE_LIBRESETPROP + Update_Patch_Level(); +#endif // Start services needed for vold decrypt LOGINFO("Starting services...\n"); @@ -1225,7 +1350,9 @@ int Vold_Decrypt_Core(const string& Password) { LOGINFO("Failed to start vold\n"); res = VD_ERR_VOLD_FAILED_TO_START; } - +#ifdef TW_INCLUDE_LIBRESETPROP + Revert_Patch_Level(); +#endif // Stop services needed for vold decrypt so /system can be unmounted LOGINFO("Stopping services...\n"); Stop_Service("sys_vold"); diff --git a/data.cpp b/data.cpp index 088dbda68a..ffb9b420d2 100755 --- a/data.cpp +++ b/data.cpp @@ -273,44 +273,6 @@ int DataManager::LoadValues(const string& filename) return 0; } -int DataManager::LoadPersistValues(void) -{ - static bool loaded = false; - string dev_id; - - // Only run this function once, and make sure normal settings file has not yet been read - if (loaded || !mBackingFile.empty() || !TWFunc::Path_Exists(PERSIST_SETTINGS_FILE)) - return -1; - - LOGINFO("Attempt to load settings from /persist settings file...\n"); - - if (!mInitialized) - SetDefaultValues(); - - GetValue("device_id", dev_id); - mPersist.SetFile(PERSIST_SETTINGS_FILE); - mPersist.SetFileVersion(FILE_VERSION); - - // Read in the file, if possible - pthread_mutex_lock(&m_valuesLock); - mPersist.LoadValues(); - -#ifndef TW_NO_SCREEN_TIMEOUT - blankTimer.setTime(mPersist.GetIntValue("tw_screen_timeout_secs")); -#endif - - update_tz_environment_variables(); - TWFunc::Set_Brightness(GetStrValue("tw_brightness")); - - pthread_mutex_unlock(&m_valuesLock); - - /* Don't set storage nor backup paths this early */ - - loaded = true; - - return 0; -} - int DataManager::Flush() { return SaveValues(); @@ -319,15 +281,6 @@ int DataManager::Flush() int DataManager::SaveValues() { #ifndef TW_OEM_BUILD - if (PartitionManager.Mount_By_Path("/persist", false)) { - mPersist.SetFile(PERSIST_SETTINGS_FILE); - mPersist.SetFileVersion(FILE_VERSION); - pthread_mutex_lock(&m_valuesLock); - mPersist.SaveValues(); - pthread_mutex_unlock(&m_valuesLock); - LOGINFO("Saved settings file values to %s\n", PERSIST_SETTINGS_FILE); - } - if (mBackingFile.empty()) return -1; @@ -510,18 +463,56 @@ int DataManager::SetValue(const string& varName, const unsigned long long& value return SetValue(varName, valStr.str(), persist); } +// For legacy code that doesn't set a scope int DataManager::SetProgress(const float Fraction) { - return SetValue("ui_progress", (float) (Fraction * 100.0)); + if (SetValue("ui_portion_size", 0) != 0) + return -1; + if (SetValue("ui_portion_start", 0) != 0) + return -1; + ShowProgress(1, 0); + int res = _SetProgress(Fraction); + if (SetValue("ui_portion_size", 0) != 0) + return -1; + if (SetValue("ui_portion_start", 0) != 0) + return -1; + return res; } -int DataManager::ShowProgress(const float Portion, const float Seconds) +int DataManager::_SetProgress(float Fraction) { + float Portion_Start, Portion_Size; + GetValue("ui_portion_size", Portion_Size); + GetValue("ui_portion_start", Portion_Start); + //LOGINFO("SetProgress(%.2lf): Portion_Size: %.2lf Portion_Start: %.2lf\n", Fraction, Portion_Size, Portion_Start); + if (Fraction < 0.0) + Fraction = 0; + if (Fraction > 1.0) + Fraction = 1; + if (SetValue("ui_progress", (float) ((Portion_Start + (Portion_Size * Fraction)) * 100.0)) != 0) + return -1; + return (SetValue("ui_progress_portion", 0) != 0); +} + +int DataManager::ShowProgress(float Portion, const float Seconds) { - float Starting_Portion; - GetValue("ui_progress_portion", Starting_Portion); - if (SetValue("ui_progress_portion", (float)(Portion * 100.0) + Starting_Portion) != 0) + float Portion_Start, Portion_Size; + GetValue("ui_portion_size", Portion_Size); + GetValue("ui_portion_start", Portion_Start); + Portion_Start += Portion_Size; + if(Portion + Portion_Start > 1.0) + Portion = 1.0 - Portion_Start; + //LOGINFO("ShowProgress(%.2lf, %.2lf): Portion_Start: %.2lf\n", Portion, Seconds, Portion_Start); + if (SetValue("ui_portion_start", Portion_Start) != 0) + return -1; + if (SetValue("ui_portion_size", Portion) != 0) return -1; - if (SetValue("ui_progress_frames", Seconds * 30) != 0) + if (SetValue("ui_progress", (float)(Portion_Start * 100.0)) != 0) return -1; + if(Seconds) { + if (SetValue("ui_progress_portion", (float)((Portion * 100.0) + Portion_Start)) != 0) + return -1; + if (SetValue("ui_progress_frames", Seconds * 48) != 0) + return -1; + } return 0; } @@ -535,7 +526,7 @@ void DataManager::SetBackupFolder() { string str = GetCurrentStoragePath(); TWPartition* partition = PartitionManager.Find_Partition_By_Path(str); - str += "/TWRP/BACKUPS/"; + str += TWFunc::Check_For_TwrpFolder() + "/BACKUPS/"; string dev_id; GetValue("device_id", dev_id); @@ -622,9 +613,11 @@ void DataManager::SetDefaultValues() mConst.SetValue(TW_SHOW_DUMLOCK, "0"); #endif + mData.SetValue(TW_RECOVERY_FOLDER_VAR, TW_DEFAULT_RECOVERY_FOLDER); + str = GetCurrentStoragePath(); mPersist.SetValue(TW_ZIP_LOCATION_VAR, str); - str += "/TWRP/BACKUPS/"; + str += DataManager::GetStrValue(TW_RECOVERY_FOLDER_VAR) + "/BACKUPS/"; string dev_id; mConst.GetValue("device_id", dev_id); @@ -726,6 +719,10 @@ void DataManager::SetDefaultValues() printf("TW_HAS_EDL_MODE := true\n"); mConst.SetValue(TW_EDL_MODE, "1"); #endif +#ifdef PRODUCT_USE_DYNAMIC_PARTITIONS + printf("PRODUCT_USE_DYNAMIC_PARTITIONS := true\n"); + mConst.SetValue(TW_FASTBOOT_MODE, "1"); +#endif #ifdef TW_INCLUDE_CRYPTO mConst.SetValue(TW_HAS_CRYPTO, "1"); printf("TW_INCLUDE_CRYPTO := true\n"); @@ -739,8 +736,12 @@ void DataManager::SetDefaultValues() #ifdef TW_HAS_NO_BOOT_PARTITION mPersist.SetValue("tw_backup_list", "/system;/data;"); +#else +#ifdef PRODUCT_USE_DYNAMIC_PARTITIONS + mPersist.SetValue("tw_backup_list", "/data;"); #else mPersist.SetValue("tw_backup_list", "/system;/data;/boot;"); +#endif #endif mConst.SetValue(TW_MIN_SYSTEM_VAR, TW_MIN_SYSTEM_SIZE); mData.SetValue(TW_BACKUP_NAME, "(Auto Generate)"); @@ -754,6 +755,7 @@ void DataManager::SetDefaultValues() mPersist.SetValue(TW_GUI_SORT_ORDER, "1"); mPersist.SetValue(TW_RM_RF_VAR, "0"); mPersist.SetValue(TW_SKIP_DIGEST_CHECK_VAR, "0"); + mPersist.SetValue(TW_SKIP_DIGEST_CHECK_ZIP_VAR, "1"); mPersist.SetValue(TW_SKIP_DIGEST_GENERATE_VAR, "0"); mPersist.SetValue(TW_SDEXT_SIZE, "0"); mPersist.SetValue(TW_SWAP_SIZE, "0"); @@ -776,6 +778,7 @@ void DataManager::SetDefaultValues() mData.SetValue("tw_background_thread_running", "0"); mData.SetValue(TW_RESTORE_FILE_DATE, "0"); mPersist.SetValue("tw_military_time", "0"); + mData.SetValue(TW_IS_SUPER, "0"); #ifdef TW_INCLUDE_CRYPTO mPersist.SetValue(TW_USE_SHA2, "1"); @@ -783,6 +786,7 @@ void DataManager::SetDefaultValues() #else mPersist.SetValue(TW_NO_SHA2, "1"); #endif + mPersist.SetValue(TW_UNMOUNT_SYSTEM, "1"); #ifdef TW_NO_SCREEN_TIMEOUT mConst.SetValue("tw_screen_timeout_secs", "0"); @@ -795,6 +799,7 @@ void DataManager::SetDefaultValues() mData.SetValue("tw_encrypt_backup", "0"); mData.SetValue("tw_sleep_total", "5"); mData.SetValue("tw_sleep", "5"); + mData.SetValue("tw_enable_fastboot", "0"); // Brightness handling string findbright; @@ -917,10 +922,19 @@ void DataManager::SetDefaultValues() mData.SetValue("tw_app_install_status", "0"); // 0 = no status, 1 = not installed, 2 = already installed mData.SetValue("tw_app_installed_in_system", "0"); #endif +#ifndef TW_EXCLUDE_NANO + mConst.SetValue("tw_include_nano", "1"); +#else + LOGINFO("TW_EXCLUDE_NANO := true\n"); + mConst.SetValue("tw_include_nano", "0"); +#endif + + mData.SetValue("tw_flash_both_slots", "0"); + mData.SetValue("tw_is_slot_part", "0"); mData.SetValue("tw_enable_adb_backup", "0"); - if (TWFunc::Path_Exists("/sbin/magiskboot")) + if (TWFunc::Path_Exists("/system/bin/magiskboot")) mConst.SetValue("tw_has_repack_tools", "1"); else mConst.SetValue("tw_has_repack_tools", "0"); @@ -1049,28 +1063,30 @@ void DataManager::Output_Version(void) string Path; char version[255]; - std::string cacheDir = TWFunc::get_cache_dir(); - if (cacheDir.empty()) { + std::string logDir = TWFunc::get_log_dir(); + if (logDir.empty()) { LOGINFO("Unable to find cache directory\n"); return; } - std::string recoveryCacheDir = cacheDir + "recovery/"; + std::string recoveryLogDir = logDir + "recovery/"; - if (cacheDir == NON_AB_CACHE_DIR) { - if (!PartitionManager.Mount_By_Path(NON_AB_CACHE_DIR, false)) { + if (logDir == CACHE_LOGS_DIR) { + if (!PartitionManager.Mount_By_Path(CACHE_LOGS_DIR, false)) { LOGINFO("Unable to mount '%s' to write version number.\n", Path.c_str()); return; } - } - 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; + + if (!TWFunc::Path_Exists(recoveryLogDir)) { + LOGINFO("Recreating %s folder.\n", recoveryLogDir.c_str()); + if (!TWFunc::Create_Dir_Recursive(recoveryLogDir.c_str(), S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP, 0, 0)) { + LOGERR("DataManager::Output_Version -- Unable to make %s: %s\n", recoveryLogDir.c_str(), strerror(errno)); + return; + } } } - std::string verPath = recoveryCacheDir + ".version"; + + std::string verPath = recoveryLogDir + ".version"; if (TWFunc::Path_Exists(verPath)) { unlink(verPath.c_str()); } @@ -1082,7 +1098,7 @@ void DataManager::Output_Version(void) strcpy(version, TW_VERSION_STR); fwrite(version, sizeof(version[0]), strlen(version) / sizeof(version[0]), fp); fclose(fp); - TWFunc::copy_file("/etc/recovery.fstab", recoveryCacheDir + "recovery.fstab", 0644); + TWFunc::copy_file("/etc/recovery.fstab", recoveryLogDir + "recovery.fstab", 0644); PartitionManager.Output_Storage_Fstab(); sync(); LOGINFO("Version number saved to '%s'\n", verPath.c_str()); @@ -1101,8 +1117,8 @@ void DataManager::ReadSettingsFile(void) memset(mkdir_path, 0, sizeof(mkdir_path)); memset(settings_file, 0, sizeof(settings_file)); - sprintf(mkdir_path, "%s/TWRP", GetSettingsStoragePath().c_str()); - sprintf(settings_file, "%s/.twrps", mkdir_path); + sprintf(mkdir_path, "%s%s", GetSettingsStoragePath().c_str(), GetStrValue(TW_RECOVERY_FOLDER_VAR).c_str()); + sprintf(settings_file, "%s/%s", mkdir_path, TW_SETTINGS_FILE); if (!PartitionManager.Mount_Settings_Storage(false)) { @@ -1142,3 +1158,11 @@ void DataManager::Vibrate(const string& varName) } #endif } + + +void DataManager::LoadTWRPFolderInfo(void) +{ + string mainPath = GetCurrentStoragePath(); + SetValue(TW_RECOVERY_FOLDER_VAR, TWFunc::Check_For_TwrpFolder()); + mBackingFile = mainPath + GetStrValue(TW_RECOVERY_FOLDER_VAR) + '/' + TW_SETTINGS_FILE; +} \ No newline at end of file diff --git a/data.hpp b/data.hpp index d61fe8e7c0..843bef2076 100755 --- a/data.hpp +++ b/data.hpp @@ -23,8 +23,6 @@ #include #include "infomanager.hpp" -#define PERSIST_SETTINGS_FILE "/persist/.twrps" - using namespace std; class DataManager @@ -34,6 +32,7 @@ class DataManager static int LoadValues(const string& filename); static int LoadPersistValues(void); static int Flush(); + static void LoadTWRPFolderInfo(void); // Core get routines static int GetValue(const string& varName, string& value); @@ -51,7 +50,8 @@ class DataManager static int SetValue(const string& varName, const float value, const int persist = 0); static int SetValue(const string& varName, const unsigned long long& value, const int persist = 0); static int SetProgress(const float Fraction); - static int ShowProgress(const float Portion, const float Seconds); + static int _SetProgress(float Fraction); + static int ShowProgress(float Portion, const float Seconds); static void DumpValues(); static void update_tz_environment_variables(); @@ -64,8 +64,10 @@ class DataManager static string GetCurrentStoragePath(void); static string GetSettingsStoragePath(void); -protected: +public: static string mBackingFile; + +protected: static int mInitialized; static InfoManager mPersist; static InfoManager mData; diff --git a/device.cpp b/device.cpp deleted file mode 100644 index f881daff63..0000000000 --- a/device.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 "device.h" - -static const char* MENU_ITEMS[] = { - "Reboot system now", - "Reboot to bootloader", - "Apply update from ADB", - "Apply update from SD card", - "Wipe data/factory reset", -#ifndef AB_OTA_UPDATER - "Wipe cache partition", -#endif // !AB_OTA_UPDATER - "Mount /system", - "View recovery logs", - "Run graphics test", - "Run locale test", - "Power off", - nullptr, -}; - -static const Device::BuiltinAction MENU_ACTIONS[] = { - Device::REBOOT, - Device::REBOOT_BOOTLOADER, - Device::APPLY_ADB_SIDELOAD, - Device::APPLY_SDCARD, - Device::WIPE_DATA, -#ifndef AB_OTA_UPDATER - Device::WIPE_CACHE, -#endif // !AB_OTA_UPDATER - Device::MOUNT_SYSTEM, - Device::VIEW_RECOVERY_LOGS, - Device::RUN_GRAPHICS_TEST, - Device::RUN_LOCALE_TEST, - Device::SHUTDOWN, -}; - -static_assert(sizeof(MENU_ITEMS) / sizeof(MENU_ITEMS[0]) == - sizeof(MENU_ACTIONS) / sizeof(MENU_ACTIONS[0]) + 1, - "MENU_ITEMS and MENU_ACTIONS should have the same length, " - "except for the extra NULL entry in MENU_ITEMS."); - -const char* const* Device::GetMenuItems() { - return MENU_ITEMS; -} - -Device::BuiltinAction Device::InvokeMenuItem(int menu_position) { - return menu_position < 0 ? NO_ACTION : MENU_ACTIONS[menu_position]; -} - -int Device::HandleMenuKey(int key, bool visible) { - if (!visible) { - return kNoAction; - } - - switch (key) { - case KEY_DOWN: - case KEY_VOLUMEDOWN: - return kHighlightDown; - - case KEY_UP: - case KEY_VOLUMEUP: - return kHighlightUp; - - case KEY_ENTER: - case KEY_POWER: - return kInvokeItem; - - default: - // If you have all of the above buttons, any other buttons - // are ignored. Otherwise, any button cycles the highlight. - return ui_->HasThreeButtons() ? kNoAction : kHighlightDown; - } -} diff --git a/device.h b/device.h deleted file mode 100644 index 74745b36c3..0000000000 --- a/device.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2011 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 _RECOVERY_DEVICE_H -#define _RECOVERY_DEVICE_H - -#include "ui.h" - -class Device { - public: - explicit Device(RecoveryUI* ui) : ui_(ui) {} - virtual ~Device() {} - - // Called to obtain the UI object that should be used to display the recovery user interface for - // this device. You should not have called Init() on the UI object already, the caller will do - // that after this method returns. - virtual RecoveryUI* GetUI() { - return ui_; - } - - // Called when recovery starts up (after the UI has been obtained and initialized and after the - // arguments have been parsed, but before anything else). - virtual void StartRecovery() {}; - - // Called from the main thread when recovery is at the main menu and waiting for input, and a key - // is pressed. (Note that "at" the main menu does not necessarily mean the menu is visible; - // recovery will be at the main menu with it invisible after an unsuccessful operation [ie OTA - // package failure], or if recovery is started with no command.) - // - // 'key' is the code of the key just pressed. (You can call IsKeyPressed() on the RecoveryUI - // object you returned from GetUI if you want to find out if other keys are held down.) - // - // 'visible' is true if the menu is visible. - // - // Returns one of the defined constants below in order to: - // - // - move the menu highlight (kHighlight{Up,Down}) - // - invoke the highlighted item (kInvokeItem) - // - do nothing (kNoAction) - // - invoke a specific action (a menu position: any non-negative number) - virtual int HandleMenuKey(int key, bool visible); - - enum BuiltinAction { - NO_ACTION = 0, - REBOOT = 1, - APPLY_SDCARD = 2, - // APPLY_CACHE was 3. - APPLY_ADB_SIDELOAD = 4, - WIPE_DATA = 5, - WIPE_CACHE = 6, - REBOOT_BOOTLOADER = 7, - SHUTDOWN = 8, - VIEW_RECOVERY_LOGS = 9, - MOUNT_SYSTEM = 10, - RUN_GRAPHICS_TEST = 11, - RUN_LOCALE_TEST = 12, - }; - - // Return the list of menu items (an array of strings, NULL-terminated). The menu_position passed - // to InvokeMenuItem will correspond to the indexes into this array. - virtual const char* const* GetMenuItems(); - - // Perform a recovery action selected from the menu. 'menu_position' will be the item number of - // the selected menu item, or a non-negative number returned from HandleMenuKey(). The menu will - // be hidden when this is called; implementations can call ui_print() to print information to the - // screen. If the menu position is one of the builtin actions, you can just return the - // corresponding enum value. If it is an action specific to your device, you actually perform it - // here and return NO_ACTION. - virtual BuiltinAction InvokeMenuItem(int menu_position); - - static const int kNoAction = -1; - static const int kHighlightUp = -2; - static const int kHighlightDown = -3; - static const int kInvokeItem = -4; - - // Called before and after we do a wipe data/factory reset operation, either via a reboot from the - // main system with the --wipe_data flag, or when the user boots into recovery image manually and - // selects the option from the menu, to perform whatever device-specific wiping actions as needed. - // Returns true on success; returning false from PreWipeData will prevent the regular wipe, and - // returning false from PostWipeData will cause the wipe to be considered a failure. - virtual bool PreWipeData() { - return true; - } - - virtual bool PostWipeData() { - return true; - } - - private: - RecoveryUI* ui_; -}; - -// The device-specific library must define this function (or the default one will be used, if there -// is no device-specific library). It returns the Device object that recovery should use. -Device* make_device(); - -#endif // _DEVICE_H diff --git a/dosfstools/Android.mk b/dosfstools/Android.mk old mode 100644 new mode 100755 index 7b6d623496..d4b85a0975 --- a/dosfstools/Android.mk +++ b/dosfstools/Android.mk @@ -19,7 +19,7 @@ LOCAL_CFLAGS += -Wno-sign-compare LOCAL_MODULE = fsck.fat LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) @@ -40,7 +40,7 @@ LOCAL_CFLAGS += -Wno-sign-compare LOCAL_MODULE = fatlabel LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) @@ -52,7 +52,7 @@ LOCAL_CFLAGS += -Wno-sign-compare LOCAL_MODULE = mkfs.fat LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin include $(BUILD_EXECUTABLE) endif diff --git a/edify/expr.cpp b/edify/expr.cpp index 6823b73392..c090eb28ac 100644 --- a/edify/expr.cpp +++ b/edify/expr.cpp @@ -51,9 +51,9 @@ bool Evaluate(State* state, const std::unique_ptr& expr, std::string* resu if (!v) { return false; } - if (v->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type); - return false; + if (v->type != Value::Type::STRING) { + ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type); + return false; } *result = v->data; @@ -68,7 +68,7 @@ Value* StringValue(const char* str) { if (str == nullptr) { return nullptr; } - return new Value(VAL_STRING, str); + return new Value(Value::Type::STRING, str); } Value* StringValue(const std::string& str) { diff --git a/edify/include/edify/expr.h b/edify/include/edify/expr.h index 770d1cf0d0..5cbd5e15d2 100644 --- a/edify/include/edify/expr.h +++ b/edify/include/edify/expr.h @@ -53,19 +53,16 @@ struct State { bool is_retry = false; }; -enum ValueType { - VAL_INVALID = -1, - VAL_STRING = 1, - VAL_BLOB = 2, -}; - struct Value { - ValueType type; - std::string data; + enum class Type { + STRING = 1, + BLOB = 2, + }; + + Value(Type type, const std::string& str) : type(type), data(str) {} - Value(ValueType type, const std::string& str) : - type(type), - data(str) {} + Type type; + std::string data; }; struct Expr; @@ -156,6 +153,6 @@ Value* StringValue(const char* str); Value* StringValue(const std::string& str); -int parse_string(const char* str, std::unique_ptr* root, int* error_count); +int ParseString(const std::string& str, std::unique_ptr* root, int* error_count); #endif // _EXPRESSION_H diff --git a/edify/parser.yy b/edify/parser.yy index bd2e0105f2..3a63c37f89 100644 --- a/edify/parser.yy +++ b/edify/parser.yy @@ -138,7 +138,7 @@ void yyerror(std::unique_ptr* root, int* error_count, const char* s) { ++*error_count; } -int parse_string(const char* str, std::unique_ptr* root, int* error_count) { - yy_switch_to_buffer(yy_scan_string(str)); - return yyparse(root, error_count); +int ParseString(const std::string& str, std::unique_ptr* root, int* error_count) { + yy_switch_to_buffer(yy_scan_string(str.c_str())); + return yyparse(root, error_count); } diff --git a/etc/Android.mk b/etc/Android.mk old mode 100644 new mode 100755 index ca549ea296..2bbc88342f --- a/etc/Android.mk +++ b/etc/Android.mk @@ -18,7 +18,7 @@ ifneq ($(TW_EXCLUDE_DEFAULT_USB_INIT), true) include $(CLEAR_VARS) LOCAL_MODULE := init.recovery.usb.rc -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES # Cannot send to TARGET_RECOVERY_ROOT_OUT since build system wipes init*.rc @@ -31,53 +31,95 @@ include $(BUILD_PREBUILT) endif -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 22; echo $$?),0) - include $(CLEAR_VARS) - LOCAL_MODULE := init.recovery.service.rc - LOCAL_MODULE_TAGS := eng - LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES - LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) +include $(CLEAR_VARS) +LOCAL_MODULE := init.recovery.service.rc +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) - LOCAL_SRC_FILES := init.recovery.service22.rc - include $(BUILD_PREBUILT) -else - include $(CLEAR_VARS) - LOCAL_MODULE := init.recovery.service.rc - LOCAL_MODULE_TAGS := eng - LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES - LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) +LOCAL_SRC_FILES := init.recovery.service22.rc +include $(BUILD_PREBUILT) - LOCAL_SRC_FILES := init.recovery.service21.rc - include $(BUILD_PREBUILT) -endif +include $(CLEAR_VARS) +LOCAL_MODULE := init.recovery.hlthchrg.rc +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) -ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) - include $(CLEAR_VARS) - LOCAL_MODULE := init.recovery.hlthchrg.rc - LOCAL_MODULE_TAGS := eng - LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES - LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) +LOCAL_SRC_FILES := init.recovery.hlthchrg26.rc +include $(BUILD_PREBUILT) - LOCAL_SRC_FILES := init.recovery.hlthchrg26.rc - include $(BUILD_PREBUILT) +include $(CLEAR_VARS) +LOCAL_MODULE := init.recovery.ldconfig.rc +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) - include $(CLEAR_VARS) - LOCAL_MODULE := init.recovery.ldconfig.rc - LOCAL_MODULE_TAGS := eng - LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES - LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) +LOCAL_SRC_FILES := init.recovery.ldconfig.rc +include $(BUILD_PREBUILT) - LOCAL_SRC_FILES := init.recovery.ldconfig.rc - include $(BUILD_PREBUILT) -else - include $(CLEAR_VARS) - LOCAL_MODULE := init.recovery.hlthchrg.rc - LOCAL_MODULE_TAGS := eng - LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES - LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) +include $(CLEAR_VARS) +LOCAL_MODULE := nano.rc +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/init - LOCAL_SRC_FILES := init.recovery.hlthchrg25.rc - include $(BUILD_PREBUILT) +LOCAL_SRC_FILES := init/$(LOCAL_MODULE) +include $(BUILD_PREBUILT) + + +ifneq ($(filter $(AB_OTA_UPDATER) $(PRODUCT_USE_DYNAMIC_PARTITIONS) $(TW_INCLUDE_CRYPTO), true),) + include $(CLEAR_VARS) + LOCAL_MODULE := hwservicemanager.rc + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES + LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/init + + LOCAL_SRC_FILES := init/$(LOCAL_MODULE) + include $(BUILD_PREBUILT) + + include $(CLEAR_VARS) + LOCAL_MODULE := vndservicemanager.rc + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES + LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/init + + LOCAL_SRC_FILES := init/$(LOCAL_MODULE) + include $(BUILD_PREBUILT) +endif +ifeq ($(AB_OTA_UPDATER),true) + include $(CLEAR_VARS) + LOCAL_MODULE := android.hardware.boot@1.0-service.rc + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES + LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/init + + LOCAL_SRC_FILES := init/$(LOCAL_MODULE) + include $(BUILD_PREBUILT) +endif + +ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true) + include $(CLEAR_VARS) + LOCAL_MODULE := android.hardware.health@2.0-service.rc + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES + LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/init + + LOCAL_SRC_FILES := init/$(LOCAL_MODULE) + include $(BUILD_PREBUILT) +endif + +ifneq ($(TW_INCLUDE_CRYPTO),) + ifneq ($(TW_INCLUDE_CRYPTO_FBE),) + include $(CLEAR_VARS) + LOCAL_MODULE := servicemanager.rc + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES + LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/init + + LOCAL_SRC_FILES := init/$(LOCAL_MODULE) + include $(BUILD_PREBUILT) + endif endif ifeq ($(TWRP_INCLUDE_LOGCAT), true) @@ -85,7 +127,7 @@ ifeq ($(TWRP_INCLUDE_LOGCAT), true) include $(CLEAR_VARS) LOCAL_MODULE := init.recovery.logd.rc - LOCAL_MODULE_TAGS := eng + LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES # Cannot send to TARGET_RECOVERY_ROOT_OUT since build system wipes init*.rc @@ -101,7 +143,7 @@ endif ifeq ($(TW_USE_TOOLBOX), true) include $(CLEAR_VARS) LOCAL_MODULE := init.recovery.mksh.rc - LOCAL_MODULE_TAGS := eng + LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES # Cannot send to TARGET_RECOVERY_ROOT_OUT since build system wipes init*.rc diff --git a/etc/init.rc b/etc/init.rc index e0889c44d5..85421962de 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -7,30 +7,29 @@ import /init.recovery.vold_decrypt.rc import /init.recovery.${ro.hardware}.rc on early-init - # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls. - write /sys/fs/selinux/checkreqprot 0 - - # Set the security context for the init process. - # This should occur before anything else (e.g. ueventd) is started. - setcon u:r:init:s0 - # Set the security context of /postinstall if present. restorecon /postinstall + # ueventd wants to write to /acct + mount cgroup none /acct cpuacct + mkdir /acct/uid start ueventd -service set_permissive /sbin/permissive.sh - oneshot - seclabel u:r:recovery:s0 - on init export PATH /sbin:/system/bin - export LD_LIBRARY_PATH /sbin + export LD_LIBRARY_PATH /system/lib64 export ANDROID_ROOT /system export ANDROID_DATA /data export EXTERNAL_STORAGE /sdcard + symlink /proc/self/fd/0 /dev/stdin + symlink /proc/self/fd/1 /dev/stdout + symlink /proc/self/fd/2 /dev/stderr + + symlink /system/bin /bin + symlink /system/etc /etc + mount cgroup none /acct cpuacct mkdir /acct/uid @@ -38,6 +37,7 @@ on init mkdir /data mkdir /cache mkdir /sideload + mkdir /mnt/system mount tmpfs tmpfs /tmp chown root shell /tmp @@ -46,12 +46,6 @@ on init write /proc/sys/kernel/panic_on_oops 1 write /proc/sys/vm/max_map_count 1000000 -on fs - mount pstore pstore /sys/fs/pstore - mkdir /dev/usb-ffs 0770 shell shell - mkdir /dev/usb-ffs/adb 0770 shell shell - mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000 - on boot ifup lo hostname localhost @@ -101,11 +95,11 @@ on late-init on property:sys.powerctl=* powerctl ${sys.powerctl} -service ueventd /sbin/ueventd +service ueventd /system/bin/ueventd critical seclabel u:r:ueventd:s0 -service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery +service adbd /system/bin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery disabled socket adbd stream 660 system system seclabel u:r:adbd:s0 @@ -116,8 +110,103 @@ on property:ro.debuggable=1 #start adbd setprop service.adb.root 1 +service fastbootd /system/bin/fastbootd + disabled + group system + seclabel u:r:fastbootd:s0 + # Restart adbd so it can run as root on property:service.adb.root=1 - write /sys/class/android_usb/android0/enable 0 restart adbd + +# Always start adbd on userdebug and eng builds +on fs && property:ro.debuggable=1 + setprop sys.usb.config adb + +on fs && property:sys.usb.configfs=1 + mount configfs none /config + mkdir /config/usb_gadget/g1 0770 shell shell + write /config/usb_gadget/g1/idVendor 0x18D1 + mkdir /config/usb_gadget/g1/strings/0x409 0770 + write /config/usb_gadget/g1/strings/0x409/serialnumber ${ro.serialno} + write /config/usb_gadget/g1/strings/0x409/manufacturer ${ro.product.manufacturer} + write /config/usb_gadget/g1/strings/0x409/product ${ro.product.model} + mkdir /config/usb_gadget/g1/functions/ffs.adb + mkdir /config/usb_gadget/g1/functions/ffs.fastboot + mkdir /config/usb_gadget/g1/configs/b.1 0777 shell shell + mkdir /config/usb_gadget/g1/configs/b.1/strings/0x409 0770 shell shell + +on fs && property:sys.usb.configfs=0 + write /sys/class/android_usb/android0/f_ffs/aliases adb,fastboot + write /sys/class/android_usb/android0/idVendor 18D1 + write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer} + write /sys/class/android_usb/android0/iProduct ${ro.product.model} + write /sys/class/android_usb/android0/iSerial ${ro.serialno} + +on fs + mount pstore pstore /sys/fs/pstore + mkdir /dev/usb-ffs 0770 shell shell + mkdir /dev/usb-ffs/adb 0770 shell shell + mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000 + mkdir /dev/usb-ffs/fastboot 0770 system system + mount functionfs fastboot /dev/usb-ffs/fastboot rmode=0770,fmode=0660,uid=1000,gid=1000 + +on property:sys.usb.config=adb + start adbd + +on property:sys.usb.config=fastboot + start fastbootd + +on property:sys.usb.config=none && property:sys.usb.configfs=0 + stop adbd + stop fastboot + write /sys/class/android_usb/android0/enable 0 + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=adb && property:sys.usb.configfs=0 + write /sys/class/android_usb/android0/idProduct D001 + write /sys/class/android_usb/android0/functions adb + write /sys/class/android_usb/android0/enable 1 + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=sideload && property:sys.usb.configfs=0 + write /sys/class/android_usb/android0/idProduct D001 + write /sys/class/android_usb/android0/functions adb + write /sys/class/android_usb/android0/enable 1 + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=fastboot && property:sys.usb.configfs=0 + write /sys/class/android_usb/android0/idProduct 4EE0 + write /sys/class/android_usb/android0/functions fastboot write /sys/class/android_usb/android0/enable 1 + setprop sys.usb.state ${sys.usb.config} + +# Configfs triggers +on property:sys.usb.config=none && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/UDC "none" + stop adbd + stop fastbootd + setprop sys.usb.ffs.ready 0 + rm /config/usb_gadget/g1/configs/b.1/f1 + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=sideload && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/idProduct 0xD001 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb" + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=adb && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/idProduct 0xD001 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb" + symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} + +on property:sys.usb.config=fastboot && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1 + write /config/usb_gadget/g1/idProduct 0x4EE0 + write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "fastboot" + symlink /config/usb_gadget/g1/functions/ffs.fastboot /config/usb_gadget/g1/configs/b.1/f1 + write /config/usb_gadget/g1/UDC ${sys.usb.controller} + setprop sys.usb.state ${sys.usb.config} diff --git a/etc/init.recovery.hlthchrg25.rc b/etc/init.recovery.hlthchrg25.rc old mode 100644 new mode 100755 index 62b24894d9..99a8baa13c --- a/etc/init.recovery.hlthchrg25.rc +++ b/etc/init.recovery.hlthchrg25.rc @@ -1,5 +1,5 @@ # healthd for pre Android 8.0 -service healthd /sbin/healthd -r +service healthd /system/bin/healthd -r critical seclabel u:r:healthd:s0 diff --git a/etc/init.recovery.ldconfig.rc b/etc/init.recovery.ldconfig.rc index ad9c296802..c99f802b32 100755 --- a/etc/init.recovery.ldconfig.rc +++ b/etc/init.recovery.ldconfig.rc @@ -1,2 +1,2 @@ on fs - export LD_CONFIG_FILE /sbin/ld.config.txt + export LD_CONFIG_FILE /system/etc/ld.config.txt diff --git a/etc/init.recovery.logd.rc b/etc/init.recovery.logd.rc index 423039cf02..29db6be8b3 100644 --- a/etc/init.recovery.logd.rc +++ b/etc/init.recovery.logd.rc @@ -1,13 +1,13 @@ -on load_all_props_action +on late-init start logd -on load_persist_props_action - start logd - -service logd /sbin/logd +service logd /system/bin/logd class core - socket logd stream 0666 logd logd - socket logdr seqpacket 0666 logd logd - socket logdw dgram 0222 logd logd - group root system + socket logdr seqpacket 0666 root root + socket logdw dgram+passcred 0222 root root + file /proc/kmsg r + file /dev/kmsg w + user root + group root + capabilities SYSLOG AUDIT_CONTROL SETGID SETUID seclabel u:r:logd:s0 diff --git a/etc/init.recovery.service21.rc b/etc/init.recovery.service21.rc deleted file mode 100644 index 892b226ffe..0000000000 --- a/etc/init.recovery.service21.rc +++ /dev/null @@ -1,4 +0,0 @@ -on boot - -# For starting recovery on 4.4 and older -service recovery /sbin/recovery diff --git a/etc/init.recovery.service22.rc b/etc/init.recovery.service22.rc old mode 100644 new mode 100755 index bb2853c1aa..410efff99a --- a/etc/init.recovery.service22.rc +++ b/etc/init.recovery.service22.rc @@ -1,5 +1,9 @@ on boot # For starting recovery on 5.0 and newer -service recovery /sbin/recovery +service recovery /system/bin/recovery + socket recovery stream 422 system system seclabel u:r:recovery:s0 + +on early-init + write /sys/fs/selinux/enforce 0 diff --git a/etc/init/android.hardware.boot@1.0-service.rc b/etc/init/android.hardware.boot@1.0-service.rc new file mode 100644 index 0000000000..e1c09152a7 --- /dev/null +++ b/etc/init/android.hardware.boot@1.0-service.rc @@ -0,0 +1,9 @@ +on post-fs + start boot-hal-1-0 + +service boot-hal-1-0 /system/bin/android.hardware.boot@1.0-service + user root + group root + setenv LD_LIBRARY_PATH /vendor/lib64:/vendor/lib:/system/lib64:/system/lib:/sbin + disabled + seclabel u:r:recovery:s0 diff --git a/etc/init/android.hardware.health@2.0-service.rc b/etc/init/android.hardware.health@2.0-service.rc new file mode 100644 index 0000000000..ef17467c10 --- /dev/null +++ b/etc/init/android.hardware.health@2.0-service.rc @@ -0,0 +1,10 @@ +on boot + start health-hal-2-0 + +service health-hal-2-0 /system/bin/android.hardware.health@2.0-service + disabled + user root + group root + capabilities WAKE_ALARM + file /dev/kmsg w + seclabel u:r:recovery:s0 diff --git a/etc/init/hwservicemanager.rc b/etc/init/hwservicemanager.rc new file mode 100644 index 0000000000..bbafb6d463 --- /dev/null +++ b/etc/init/hwservicemanager.rc @@ -0,0 +1,9 @@ +on init + start hwservicemanager + +service hwservicemanager /system/bin/hwservicemanager + user root + group root + onrestart setprop hwservicemanager.ready false + disabled + seclabel u:r:recovery:s0 diff --git a/etc/init/nano.rc b/etc/init/nano.rc new file mode 100644 index 0000000000..f57f656406 --- /dev/null +++ b/etc/init/nano.rc @@ -0,0 +1,3 @@ +on fs + export TERMINFO /system/etc/terminfo + export TERM pcansi diff --git a/etc/init/servicemanager.rc b/etc/init/servicemanager.rc new file mode 100644 index 0000000000..40ed84d82e --- /dev/null +++ b/etc/init/servicemanager.rc @@ -0,0 +1,8 @@ +on init + start servicemanager + +service servicemanager /system/bin/servicemanager + user root + group root readproc + disabled + seclabel u:r:recovery:s0 diff --git a/etc/init/vndservicemanager.rc b/etc/init/vndservicemanager.rc new file mode 100644 index 0000000000..149a378abd --- /dev/null +++ b/etc/init/vndservicemanager.rc @@ -0,0 +1,10 @@ +on init + start vndservicemanager + +service vndservicemanager /system/bin/vndservicemanager /dev/vndbinder + disabled + user root + group root readproc + writepid /dev/cpuset/system-background/tasks + shutdown critical + seclabel u:r:recovery:s0 diff --git a/exfat/fsck/Android.mk b/exfat/fsck/Android.mk old mode 100644 new mode 100755 index 64ebc0fa2b..085dda40f1 --- a/exfat/fsck/Android.mk +++ b/exfat/fsck/Android.mk @@ -5,7 +5,7 @@ LOCAL_MODULE := fsckexfat LOCAL_MODULE_STEM := fsck.exfat LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -Wno-sign-compare LOCAL_SRC_FILES = main.c LOCAL_C_INCLUDES += $(LOCAL_PATH) \ diff --git a/exfat/fuse/Android.mk b/exfat/fuse/Android.mk old mode 100644 new mode 100755 index 57f35be5d1..b0cca7d0ef --- a/exfat/fuse/Android.mk +++ b/exfat/fuse/Android.mk @@ -4,7 +4,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := exfat-fuse LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -Wno-sign-compare -Wno-unused-parameter LOCAL_SRC_FILES = main.c LOCAL_C_INCLUDES += $(LOCAL_PATH) \ diff --git a/exfat/mkfs/Android.mk b/exfat/mkfs/Android.mk old mode 100644 new mode 100755 index 9f3e7cb5b5..e1158f20d5 --- a/exfat/mkfs/Android.mk +++ b/exfat/mkfs/Android.mk @@ -4,7 +4,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := mkexfatfs LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -Wno-sign-compare LOCAL_SRC_FILES = cbm.c fat.c main.c mkexfat.c rootdir.c uct.c uctc.c vbr.c LOCAL_C_INCLUDES += $(LOCAL_PATH) \ diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp new file mode 100644 index 0000000000..14f5e4bdc7 --- /dev/null +++ b/fastboot/fastboot.cpp @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#include "fastboot.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "recovery_ui/ui.h" + +static const std::vector> kFastbootMenuActions{ + { "Reboot system now", Device::REBOOT }, + { "Enter recovery", Device::ENTER_RECOVERY }, + { "Reboot to bootloader", Device::REBOOT_BOOTLOADER }, + { "Power off", Device::SHUTDOWN }, +}; + +Device::BuiltinAction StartFastboot(Device* device, const std::vector& /* args */) { + RecoveryUI* ui = device->GetUI(); + + std::vector title_lines = { "Android Fastboot" }; + title_lines.push_back("Product name - " + android::base::GetProperty("ro.product.device", "")); + title_lines.push_back("Bootloader version - " + android::base::GetProperty("ro.bootloader", "")); + title_lines.push_back("Baseband version - " + + android::base::GetProperty("ro.build.expect.baseband", "")); + title_lines.push_back("Serial number - " + android::base::GetProperty("ro.serialno", "")); + title_lines.push_back(std::string("Secure boot - ") + + ((android::base::GetProperty("ro.secure", "") == "1") ? "yes" : "no")); + title_lines.push_back("HW version - " + android::base::GetProperty("ro.revision", "")); + + ui->ResetKeyInterruptStatus(); + ui->SetTitle(title_lines); + ui->ShowText(true); + + // Reset to normal system boot so recovery won't cycle indefinitely. + // TODO(b/112277594) Clear only if 'recovery' field of BCB is empty. If not, + // set the 'command' field of BCB to 'boot-recovery' so the next boot is into recovery + // to finish any interrupted tasks. + std::string err; + if (!clear_bootloader_message(&err)) { + LOG(ERROR) << "Failed to clear BCB message: " << err; + } + + std::vector fastboot_menu_items; + std::transform(kFastbootMenuActions.cbegin(), kFastbootMenuActions.cend(), + std::back_inserter(fastboot_menu_items), + [](const auto& entry) { return entry.first; }); + + auto chosen_item = ui->ShowMenu( + {}, fastboot_menu_items, 0, false, + std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); + + if (chosen_item == static_cast(RecoveryUI::KeyError::INTERRUPTED)) { + return Device::KEY_INTERRUPTED; + } + if (chosen_item == static_cast(RecoveryUI::KeyError::TIMED_OUT)) { + return Device::BuiltinAction::NO_ACTION; + } + return kFastbootMenuActions[chosen_item].second; +} diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h new file mode 100644 index 0000000000..1aa7de66e4 --- /dev/null +++ b/fastboot/fastboot.h @@ -0,0 +1,24 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +#include "recovery_ui/device.h" + +Device::BuiltinAction StartFastboot(Device* device, const std::vector& args); diff --git a/fb2png/Android.mk b/fb2png/Android.mk index e82495d515..8d19868985 100644 --- a/fb2png/Android.mk +++ b/fb2png/Android.mk @@ -62,7 +62,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := main.c LOCAL_MODULE := fb2png LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS += -DANDROID LOCAL_STATIC_LIBRARIES := libfb2png libpng libz libc diff --git a/flashutils/Android.mk b/flashutils/Android.mk old mode 100644 new mode 100755 index ab552b1ed5..0a0450924d --- a/flashutils/Android.mk +++ b/flashutils/Android.mk @@ -81,7 +81,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES := flashutils.c LOCAL_MODULE := libflashutils -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional LOCAL_C_INCLUDES += $(commands_recovery_local_path) LOCAL_SHARED_LIBRARIES := libc libmtdutils libmmcutils libbmlutils libcrecovery @@ -97,27 +97,27 @@ include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := flash_image -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin LOCAL_SRC_FILES := flash_image.c LOCAL_SHARED_LIBRARIES := libmtdutils libflashutils libmmcutils libbmlutils libcutils libc include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_MODULE := dump_image -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin LOCAL_SRC_FILES := dump_image.c LOCAL_SHARED_LIBRARIES := libmtdutils libflashutils libmmcutils libbmlutils libcutils libc include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_MODULE := erase_image -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin LOCAL_SRC_FILES := erase_image.c LOCAL_SHARED_LIBRARIES := libmtdutils libflashutils libmmcutils libbmlutils libcutils libc include $(BUILD_EXECUTABLE) diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp new file mode 100644 index 0000000000..e74f8ba6fa --- /dev/null +++ b/fsck_unshare_blocks.cpp @@ -0,0 +1,151 @@ +/* + * 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. + */ + +#include "fsck_unshare_blocks.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "otautil/roots.h" + +static constexpr const char* SYSTEM_E2FSCK_BIN = "/system/bin/e2fsck_static"; +static constexpr const char* TMP_E2FSCK_BIN = "/tmp/e2fsck.bin"; + +static bool copy_file(const char* source, const char* dest) { + android::base::unique_fd source_fd(open(source, O_RDONLY)); + if (source_fd < 0) { + PLOG(ERROR) << "open %s failed" << source; + return false; + } + + android::base::unique_fd dest_fd(open(dest, O_CREAT | O_WRONLY, S_IRWXU)); + if (dest_fd < 0) { + PLOG(ERROR) << "open %s failed" << dest; + return false; + } + + for (;;) { + char buf[4096]; + ssize_t rv = read(source_fd, buf, sizeof(buf)); + if (rv < 0) { + PLOG(ERROR) << "read failed"; + return false; + } + if (rv == 0) { + break; + } + if (write(dest_fd, buf, rv) != rv) { + PLOG(ERROR) << "write failed"; + return false; + } + } + return true; +} + +static bool run_e2fsck(const std::string& partition) { + Volume* volume = volume_for_mount_point(partition); + if (!volume) { + LOG(INFO) << "No fstab entry for " << partition << ", skipping."; + return true; + } + + LOG(INFO) << "Running e2fsck on device " << volume->blk_device; + + std::vector args = { TMP_E2FSCK_BIN, "-p", "-E", "unshare_blocks", + volume->blk_device }; + std::vector argv(args.size()); + std::transform(args.cbegin(), args.cend(), argv.begin(), + [](const std::string& arg) { return const_cast(arg.c_str()); }); + argv.push_back(nullptr); + + pid_t child; + char* env[] = { nullptr }; + if (posix_spawn(&child, argv[0], nullptr, nullptr, argv.data(), env)) { + PLOG(ERROR) << "posix_spawn failed"; + return false; + } + + int status = 0; + int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0)); + if (ret < 0) { + PLOG(ERROR) << "waitpid failed"; + return false; + } + if (!WIFEXITED(status)) { + LOG(ERROR) << "e2fsck exited abnormally: " << status; + return false; + } + int return_code = WEXITSTATUS(status); + if (return_code >= 8) { + LOG(ERROR) << "e2fsck could not unshare blocks: " << return_code; + return false; + } + + LOG(INFO) << "Successfully unshared blocks on " << partition; + return true; +} + +bool do_fsck_unshare_blocks() { + // List of partitions we will try to e2fsck -E unshare_blocks. + std::vector partitions = { "/odm", "/oem", "/product", "/vendor" }; + + // Temporarily mount system so we can copy e2fsck_static. + std::string system_root = get_system_root(); + bool mounted = ensure_path_mounted_at(system_root, "/mnt/system") != -1; + partitions.push_back(system_root); + + if (!mounted) { + LOG(ERROR) << "Failed to mount system image."; + return false; + } + if (!copy_file(SYSTEM_E2FSCK_BIN, TMP_E2FSCK_BIN)) { + LOG(ERROR) << "Could not copy e2fsck to /tmp."; + return false; + } + if (umount("/mnt/system") < 0) { + PLOG(ERROR) << "umount failed"; + return false; + } + + bool ok = true; + for (const auto& partition : partitions) { + ok &= run_e2fsck(partition); + } + + if (ok) { + LOG(INFO) << "Finished running e2fsck."; + } else { + LOG(ERROR) << "Finished running e2fsck, but not all partitions succceeded."; + } + return ok; +} diff --git a/minadbd21/fuse_adb_provider.h b/fsck_unshare_blocks.h similarity index 75% rename from minadbd21/fuse_adb_provider.h rename to fsck_unshare_blocks.h index 0eb1f79d14..9de8ef9a3d 100644 --- a/minadbd21/fuse_adb_provider.h +++ b/fsck_unshare_blocks.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * 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. @@ -14,9 +14,9 @@ * limitations under the License. */ -#ifndef __FUSE_ADB_PROVIDER_H -#define __FUSE_ADB_PROVIDER_H +#ifndef _FILESYSTEM_CMDS_H +#define _FILESYSTEM_CMDS_H -int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size); +bool do_fsck_unshare_blocks(); -#endif +#endif // _FILESYSTEM_CMDS_H diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp deleted file mode 100644 index 46bdf17748..0000000000 --- a/fuse_sdcard_provider.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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. - */ - -#include "fuse_sdcard_provider.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "fuse_sideload.h" - -struct file_data { - int fd; // the underlying sdcard file - - uint64_t file_size; - uint32_t block_size; -}; - -static int read_block_file(const file_data& fd, uint32_t block, uint8_t* buffer, - uint32_t fetch_size) { - off64_t offset = static_cast(block) * fd.block_size; - if (TEMP_FAILURE_RETRY(lseek64(fd.fd, offset, SEEK_SET)) == -1) { - fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno)); - return -EIO; - } - - if (!android::base::ReadFully(fd.fd, buffer, fetch_size)) { - fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno)); - return -EIO; - } - - return 0; -} - -bool start_sdcard_fuse(const char* path) { - struct stat sb; - if (stat(path, &sb) == -1) { - fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno)); - return false; - } - - file_data fd; - fd.fd = open(path, O_RDONLY); - if (fd.fd == -1) { - fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); - return false; - } - fd.file_size = sb.st_size; - fd.block_size = 65536; - - provider_vtab vtab; - vtab.read_block = std::bind(&read_block_file, fd, std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3); - vtab.close = [&fd]() { close(fd.fd); }; - - // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so - // that our open file continues to work but new references see it as unmounted. - umount2("/sdcard", MNT_DETACH); - - return run_fuse_sideload(vtab, fd.file_size, fd.block_size) == 0; -} diff --git a/fuse_sideload/Android.bp b/fuse_sideload/Android.bp new file mode 100644 index 0000000000..8548548d2e --- /dev/null +++ b/fuse_sideload/Android.bp @@ -0,0 +1,41 @@ +// 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. + +cc_library { + name: "libfusesideload", + recovery_available: true, + + defaults: [ + "recovery_defaults", + ], + + cflags: [ + "-D_XOPEN_SOURCE", + "-D_GNU_SOURCE", + ], + + srcs: [ + "fuse_provider.cpp", + "fuse_sideload.cpp", + ], + + export_include_dirs: [ + "include", + ], + + shared_libs: [ + "libbase", + "libcrypto", + ], +} diff --git a/fuse_sideload/fuse_provider.cpp b/fuse_sideload/fuse_provider.cpp new file mode 100644 index 0000000000..58786f5f3a --- /dev/null +++ b/fuse_sideload/fuse_provider.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 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 "fuse_provider.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "fuse_sideload.h" + +FuseFileDataProvider::FuseFileDataProvider(const std::string& path, uint32_t block_size) { + struct stat sb; + if (stat(path.c_str(), &sb) == -1) { + fprintf(stderr, "failed to stat %s: %s\n", path.c_str(), strerror(errno)); + return; + } + + fd_.reset(open(path.c_str(), O_RDONLY)); + if (fd_ == -1) { + fprintf(stderr, "failed to open %s: %s\n", path.c_str(), strerror(errno)); + return; + } + file_size_ = sb.st_size; + fuse_block_size_ = block_size; +} + +bool FuseFileDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, + uint32_t start_block) const { + uint64_t offset = static_cast(start_block) * fuse_block_size_; + if (fetch_size > file_size_ || offset > file_size_ - fetch_size) { + fprintf(stderr, + "Out of bound read, start block: %" PRIu32 ", fetch size: %" PRIu32 + ", file size %" PRIu64 "\n", + start_block, fetch_size, file_size_); + return false; + } + + if (!android::base::ReadFullyAtOffset(fd_, buffer, fetch_size, offset)) { + fprintf(stderr, "Failed to read fetch size: %" PRIu32 " bytes data at offset %" PRIu64 ": %s\n", + fetch_size, offset, strerror(errno)); + return false; + } + + return true; +} + +void FuseFileDataProvider::Close() { + fd_.reset(); +} diff --git a/fuse_sideload.cpp b/fuse_sideload/fuse_sideload.cpp similarity index 85% rename from fuse_sideload.cpp rename to fuse_sideload/fuse_sideload.cpp index 45c79f90b9..3d9480309c 100644 --- a/fuse_sideload.cpp +++ b/fuse_sideload/fuse_sideload.cpp @@ -45,9 +45,8 @@ #include #include -#include -#include "fuse.h" -#include +#include // PATH_MAX +#include #include #include #include @@ -58,19 +57,13 @@ #include #include -#ifdef USE_MINCRYPT -#include "mincrypt/sha256.h" -#define SHA256_DIGEST_LENGTH SHA256_DIGEST_SIZE -#else -#include -#endif - #include #include #include -//#include -//#include +#include +#include +#include static constexpr uint64_t PACKAGE_FILE_ID = FUSE_ROOT_ID + 1; static constexpr uint64_t EXIT_FLAG_ID = FUSE_ROOT_ID + 2; @@ -80,14 +73,10 @@ static constexpr int NO_STATUS_EXIT = 2; using SHA256Digest = std::array; -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - struct fuse_data { - int ffd; // file descriptor for the fuse socket + android::base::unique_fd ffd; // file descriptor for the fuse socket - provider_vtab vtab; + FuseDataProvider* provider; // Provider of the source data. uint64_t file_size; // bytes @@ -102,8 +91,8 @@ struct fuse_data { uint8_t* extra_block; // another block of storage for reads that span two blocks - uint8_t* hashes; // SHA-256 hash of each block (all zeros - // if block hasn't been read yet) + std::vector + hashes; // SHA-256 hash of each block (all zeros if block hasn't been read yet) }; static void fuse_reply(const fuse_data* fd, uint64_t unique, const void* data, size_t len) { @@ -162,7 +151,7 @@ static int handle_init(void* data, fuse_data* fd, const fuse_in_header* hdr) { static void fill_attr(fuse_attr* attr, const fuse_data* fd, uint64_t nodeid, uint64_t size, uint32_t mode) { - memset(attr, 0, sizeof(*attr)); + *attr = {}; attr->nlink = 1; attr->uid = fd->uid; attr->gid = fd->gid; @@ -175,8 +164,7 @@ static void fill_attr(fuse_attr* attr, const fuse_data* fd, uint64_t nodeid, uin } static int handle_getattr(void* /* data */, const fuse_data* fd, const fuse_in_header* hdr) { - struct fuse_attr_out out; - memset(&out, 0, sizeof(out)); + fuse_attr_out out = {}; out.attr_valid = 10; if (hdr->nodeid == FUSE_ROOT_ID) { @@ -196,8 +184,7 @@ static int handle_getattr(void* /* data */, const fuse_data* fd, const fuse_in_h static int handle_lookup(void* data, const fuse_data* fd, const fuse_in_header* hdr) { if (data == nullptr) return -ENOENT; - struct fuse_entry_out out; - memset(&out, 0, sizeof(out)); + fuse_entry_out out = {}; out.entry_valid = 10; out.attr_valid = 10; @@ -222,8 +209,7 @@ static int handle_open(void* /* data */, const fuse_data* fd, const fuse_in_head if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM; if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; - struct fuse_open_out out; - memset(&out, 0, sizeof(out)); + fuse_open_out out = {}; out.fh = 10; // an arbitrary number; we always use the same handle fuse_reply(fd, hdr->unique, &out, sizeof(out)); return NO_STATUS; @@ -250,7 +236,7 @@ static int fetch_block(fuse_data* fd, uint32_t block) { return 0; } - size_t fetch_size = fd->block_size; + uint32_t fetch_size = fd->block_size; if (block * fd->block_size + fetch_size > fd->file_size) { // If we're reading the last (partial) block of the file, expect a shorter response from the // host, and pad the rest of the block with zeroes. @@ -258,8 +244,9 @@ static int fetch_block(fuse_data* fd, uint32_t block) { memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size); } - int result = fd->vtab.read_block(block, fd->block_data, fetch_size); - if (result < 0) return result; + if (!fd->provider->ReadBlockAlignedData(fd->block_data, fetch_size, block)) { + return -EIO; + } fd->curr_block = block; @@ -270,26 +257,22 @@ static int fetch_block(fuse_data* fd, uint32_t block) { // time we've read this block). // - Otherwise, return -EINVAL for the read. - uint8_t hash[SHA256_DIGEST_LENGTH]; -#ifdef USE_MINCRYPT - SHA256_hash(fd->block_data, fd->block_size, hash); -#else - SHA256(fd->block_data, fd->block_size, hash); -#endif - uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_LENGTH; - if (memcmp(hash, blockhash, SHA256_DIGEST_LENGTH) == 0) { + SHA256Digest hash; + SHA256(fd->block_data, fd->block_size, hash.data()); + + const SHA256Digest& blockhash = fd->hashes[block]; + if (hash == blockhash) { return 0; } - int i; - for (i = 0; i < SHA256_DIGEST_LENGTH; ++i) { - if (blockhash[i] != 0) { + for (uint8_t i : blockhash) { + if (i != 0) { fd->curr_block = -1; return -EIO; } } - memcpy(blockhash, hash, SHA256_DIGEST_LENGTH); + fd->hashes[block] = hash; return 0; } @@ -358,12 +341,14 @@ static int handle_read(void* data, fuse_data* fd, const fuse_in_header* hdr) { return NO_STATUS; } -int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size, - const char* mount_point) { +int run_fuse_sideload(std::unique_ptr&& provider, const char* mount_point) { // If something's already mounted on our mountpoint, try to remove it. (Mostly in case of a // previous abnormal exit.) umount2(mount_point, MNT_FORCE); + uint64_t file_size = provider->file_size(); + uint32_t block_size = provider->fuse_block_size(); + // fs/fuse/inode.c in kernel code uses the greater of 4096 and the passed-in max_read. if (block_size < 4096) { fprintf(stderr, "block size (%u) is too small\n", block_size); @@ -374,9 +359,8 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl return -1; } - fuse_data fd; - memset(&fd, 0, sizeof(fd)); - fd.vtab = vtab; + fuse_data fd = {}; + fd.provider = provider.get(); fd.file_size = file_size; fd.block_size = block_size; fd.file_blocks = (file_size == 0) ? 0 : (((file_size - 1) / block_size) + 1); @@ -388,14 +372,8 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl goto done; } - fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_LENGTH); - if (fd.hashes == NULL) { - fprintf(stderr, "failed to allocate %d bites for hashes\n", - fd.file_blocks * SHA256_DIGEST_LENGTH); - result = -1; - goto done; - } - + // All hashes will be zero-initialized. + fd.hashes.resize(fd.file_blocks); fd.uid = getuid(); fd.gid = getgid(); @@ -413,23 +391,21 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl goto done; } - fd.ffd = open("/dev/fuse", O_RDWR); - if (!fd.ffd) { + fd.ffd.reset(open("/dev/fuse", O_RDWR)); + if (fd.ffd == -1) { perror("open /dev/fuse"); result = -1; goto done; } { - char opts[256]; - snprintf(opts, sizeof(opts), - ("fd=%d,user_id=%d,group_id=%d,max_read=%u," - "allow_other,rootmode=040000"), - fd.ffd, fd.uid, fd.gid, block_size); - - result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT, "fuse", - MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts); - if (result < 0) { + std::string opts = android::base::StringPrintf( + "fd=%d,user_id=%d,group_id=%d,max_read=%u,allow_other,rootmode=040000", fd.ffd.get(), + fd.uid, fd.gid, block_size); + + result = mount("/dev/fuse", mount_point, "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, + opts.c_str()); + if (result == -1) { perror("mount"); goto done; } @@ -506,7 +482,7 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl } done: - fd.vtab.close(); + provider->Close(); if (umount2(mount_point, MNT_DETACH) == -1) { fprintf(stderr, "fuse_sideload umount failed: %s\n", strerror(errno)); @@ -517,9 +493,3 @@ int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t bl return result; } - -extern "C" int run_old_fuse_sideload(const struct provider_vtab& vtab, void* cookie __unused, - uint64_t file_size, uint32_t block_size) -{ - return run_fuse_sideload(vtab, file_size, block_size, FUSE_SIDELOAD_HOST_MOUNTPOINT); -} diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h new file mode 100644 index 0000000000..59059cf9b3 --- /dev/null +++ b/fuse_sideload/include/fuse_provider.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 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 + +#include + +// This is the base class to read data from source and provide the data to FUSE. +class FuseDataProvider { + public: + FuseDataProvider(uint64_t file_size, uint32_t block_size) + : file_size_(file_size), fuse_block_size_(block_size) {} + + virtual ~FuseDataProvider() = default; + + uint64_t file_size() const { + return file_size_; + } + uint32_t fuse_block_size() const { + return fuse_block_size_; + } + + // Reads |fetch_size| bytes data starting from |start_block|. Puts the result in |buffer|. + virtual bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, + uint32_t start_block) const = 0; + + virtual void Close() {} + + protected: + FuseDataProvider() = default; + + // Size in bytes of the file to read. + uint64_t file_size_ = 0; + // Block size passed to the fuse, this is different from the block size of the block device. + uint32_t fuse_block_size_ = 0; +}; + +// This class reads data from a file. +class FuseFileDataProvider : public FuseDataProvider { + public: + FuseFileDataProvider(const std::string& path, uint32_t block_size); + + bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, + uint32_t start_block) const override; + + bool Valid() const { + return fd_ != -1; + } + + void Close() override; + + private: + // The underlying source to read data from. + android::base::unique_fd fd_; +}; diff --git a/fuse_sideload.h b/fuse_sideload/include/fuse_sideload.h similarity index 67% rename from fuse_sideload.h rename to fuse_sideload/include/fuse_sideload.h index 1ea1eb8d85..1b7759a7fe 100644 --- a/fuse_sideload.h +++ b/fuse_sideload/include/fuse_sideload.h @@ -17,11 +17,9 @@ #ifndef __FUSE_SIDELOAD_H #define __FUSE_SIDELOAD_H -#ifdef USE_FUSE_SIDELOAD22 -#include "fuse_sideload22.h" -#else +#include -#include +#include "fuse_provider.h" // Define the filenames created by the sideload FUSE filesystem. static constexpr const char* FUSE_SIDELOAD_HOST_MOUNTPOINT = "/sideload"; @@ -30,26 +28,7 @@ static constexpr const char* FUSE_SIDELOAD_HOST_PATHNAME = "/sideload/package.zi static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_FLAG = "exit"; static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_PATHNAME = "/sideload/exit"; -struct provider_vtab { - // read a block - std::function read_block; - - // close down - std::function close; -}; - -int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size, +int run_fuse_sideload(std::unique_ptr&& provider, const char* mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT); -#ifdef __cplusplus -extern "C" { -#endif -int run_old_fuse_sideload(const struct provider_vtab& vtab, void* cookie, - uint64_t file_size, uint32_t block_size); -#ifdef __cplusplus -} -#endif - -#endif - #endif diff --git a/fuse_sideload22.cpp b/fuse_sideload22.cpp deleted file mode 100644 index f57d479b06..0000000000 --- a/fuse_sideload22.cpp +++ /dev/null @@ -1,544 +0,0 @@ -/* - * 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. - */ - -// This module creates a special filesystem containing two files. -// -// "/sideload/package.zip" appears to be a normal file, but reading -// from it causes data to be fetched from the adb host. We can use -// this to sideload packages over an adb connection without having to -// store the entire package in RAM on the device. -// -// Because we may not trust the adb host, this filesystem maintains -// the following invariant: each read of a given position returns the -// same data as the first read at that position. That is, once a -// section of the file is read, future reads of that section return -// the same data. (Otherwise, a malicious adb host process could -// return one set of bits when the package is read for signature -// verification, and then different bits for when the package is -// accessed by the installer.) If the adb host returns something -// different than it did on the first read, the reader of the file -// will see their read fail with EINVAL. -// -// The other file, "/sideload/exit", is used to control the subprocess -// that creates this filesystem. Calling stat() on the exit file -// causes the filesystem to be unmounted and the adb process on the -// device shut down. -// -// Note that only the minimal set of file operations needed for these -// two files is implemented. In particular, you can't opendir() or -// readdir() on the "/sideload" directory; ls on it won't work. - -#include -#include -#include -#include -#include -#include "fuse.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef USE_MINCRYPT -#include "mincrypt/sha256.h" -#define SHA256_DIGEST_LENGTH SHA256_DIGEST_SIZE -#else -#include -#endif - -#include "fuse_sideload.h" - -#define PACKAGE_FILE_ID (FUSE_ROOT_ID+1) -#define EXIT_FLAG_ID (FUSE_ROOT_ID+2) - -#define NO_STATUS 1 -#define NO_STATUS_EXIT 2 - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -struct fuse_data { - int ffd; // file descriptor for the fuse socket - - struct provider_vtab* vtab; - void* cookie; - - uint64_t file_size; // bytes - - uint32_t block_size; // block size that the adb host is using to send the file to us - uint32_t file_blocks; // file size in block_size blocks - - uid_t uid; - gid_t gid; - - uint32_t curr_block; // cache the block most recently read from the host - uint8_t* block_data; - - uint8_t* extra_block; // another block of storage for reads that - // span two blocks - - uint8_t* hashes; // SHA-256 hash of each block (all zeros - // if block hasn't been read yet) -}; - -static void fuse_reply(struct fuse_data* fd, __u64 unique, const void *data, size_t len) -{ - struct fuse_out_header hdr; - struct iovec vec[2]; - int res; - - hdr.len = len + sizeof(hdr); - hdr.error = 0; - hdr.unique = unique; - - vec[0].iov_base = &hdr; - vec[0].iov_len = sizeof(hdr); - vec[1].iov_base = /* const_cast */(void*)(data); - vec[1].iov_len = len; - - res = writev(fd->ffd, vec, 2); - if (res < 0) { - printf("*** REPLY FAILED *** %s\n", strerror(errno)); - } -} - -static int handle_init(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - const struct fuse_init_in* req = reinterpret_cast(data); - struct fuse_init_out out; - size_t fuse_struct_size; - - - /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out - * defined (fuse version 7.6). The structure is the same from 7.6 through - * 7.22. Beginning with 7.23, the structure increased in size and added - * new parameters. - */ - if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) { - printf("Fuse kernel version mismatch: Kernel version %d.%d, Expected at least %d.6", - req->major, req->minor, FUSE_KERNEL_VERSION); - return -1; - } - - out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION); - fuse_struct_size = sizeof(out); -#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE) - /* FUSE_KERNEL_VERSION >= 23. */ - - /* If the kernel only works on minor revs older than or equal to 22, - * then use the older structure size since this code only uses the 7.22 - * version of the structure. */ - if (req->minor <= 22) { - fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE; - } -#endif - - out.major = FUSE_KERNEL_VERSION; - out.max_readahead = req->max_readahead; - out.flags = 0; - out.max_background = 32; - out.congestion_threshold = 32; - out.max_write = 4096; - fuse_reply(fd, hdr->unique, &out, fuse_struct_size); - - return NO_STATUS; -} - -static void fill_attr(struct fuse_attr* attr, struct fuse_data* fd, - uint64_t nodeid, uint64_t size, uint32_t mode) { - memset(attr, 0, sizeof(*attr)); - attr->nlink = 1; - attr->uid = fd->uid; - attr->gid = fd->gid; - attr->blksize = 4096; - - attr->ino = nodeid; - attr->size = size; - attr->blocks = (size == 0) ? 0 : (((size-1) / attr->blksize) + 1); - attr->mode = mode; -} - -static int handle_getattr(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) { - struct fuse_attr_out out; - memset(&out, 0, sizeof(out)); - out.attr_valid = 10; - - if (hdr->nodeid == FUSE_ROOT_ID) { - fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555); - } else if (hdr->nodeid == PACKAGE_FILE_ID) { - fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); - } else if (hdr->nodeid == EXIT_FLAG_ID) { - fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); - } else { - return -ENOENT; - } - - fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; -} - -static int handle_lookup(void* data, struct fuse_data* fd, - const struct fuse_in_header* hdr) { - struct fuse_entry_out out; - memset(&out, 0, sizeof(out)); - out.entry_valid = 10; - out.attr_valid = 10; - - if (strncmp(FUSE_SIDELOAD_HOST_FILENAME, reinterpret_cast(data), - sizeof(FUSE_SIDELOAD_HOST_FILENAME)) == 0) { - out.nodeid = PACKAGE_FILE_ID; - out.generation = PACKAGE_FILE_ID; - fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); - } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, reinterpret_cast(data), - sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) { - out.nodeid = EXIT_FLAG_ID; - out.generation = EXIT_FLAG_ID; - fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); - } else { - return -ENOENT; - } - - fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; -} - -static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) { - if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM; - if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; - - struct fuse_open_out out; - memset(&out, 0, sizeof(out)); - out.fh = 10; // an arbitrary number; we always use the same handle - fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return NO_STATUS; -} - -static int handle_flush(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - return 0; -} - -static int handle_release(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - return 0; -} - -// Fetch a block from the host into fd->curr_block and fd->block_data. -// Returns 0 on successful fetch, negative otherwise. -static int fetch_block(struct fuse_data* fd, uint32_t block) { - if (block == fd->curr_block) { - return 0; - } - - if (block >= fd->file_blocks) { - memset(fd->block_data, 0, fd->block_size); - fd->curr_block = block; - return 0; - } - - size_t fetch_size = fd->block_size; - if (block * fd->block_size + fetch_size > fd->file_size) { - // If we're reading the last (partial) block of the file, - // expect a shorter response from the host, and pad the rest - // of the block with zeroes. - fetch_size = fd->file_size - (block * fd->block_size); - memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size); - } - - int result = fd->vtab->read_block(fd->cookie, block, fd->block_data, fetch_size); - if (result < 0) return result; - - fd->curr_block = block; - - // Verify the hash of the block we just got from the host. - // - // - If the hash of the just-received data matches the stored hash - // for the block, accept it. - // - If the stored hash is all zeroes, store the new hash and - // accept the block (this is the first time we've read this - // block). - // - Otherwise, return -EINVAL for the read. - - uint8_t hash[SHA256_DIGEST_LENGTH]; -#ifdef USE_MINCRYPT - SHA256_hash(fd->block_data, fd->block_size, hash); -#else - SHA256(fd->block_data, fd->block_size, hash); -#endif - uint8_t* blockhash = fd->hashes + block * SHA256_DIGEST_LENGTH; - if (memcmp(hash, blockhash, SHA256_DIGEST_LENGTH) == 0) { - return 0; - } - - int i; - for (i = 0; i < SHA256_DIGEST_LENGTH; ++i) { - if (blockhash[i] != 0) { - fd->curr_block = -1; - return -EIO; - } - } - - memcpy(blockhash, hash, SHA256_DIGEST_LENGTH); - return 0; -} - -static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { - const struct fuse_read_in* req = reinterpret_cast(data); - struct fuse_out_header outhdr; - struct iovec vec[3]; - int vec_used; - int result; - - if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; - - uint64_t offset = req->offset; - uint32_t size = req->size; - - // The docs on the fuse kernel interface are vague about what to - // do when a read request extends past the end of the file. We - // can return a short read -- the return structure does include a - // length field -- but in testing that caused the program using - // the file to segfault. (I speculate that this is due to the - // reading program accessing it via mmap; maybe mmap dislikes when - // you return something short of a whole page?) To fix this we - // zero-pad reads that extend past the end of the file so we're - // always returning exactly as many bytes as were requested. - // (Users of the mapped file have to know its real length anyway.) - - outhdr.len = sizeof(outhdr) + size; - outhdr.error = 0; - outhdr.unique = hdr->unique; - vec[0].iov_base = &outhdr; - vec[0].iov_len = sizeof(outhdr); - - uint32_t block = offset / fd->block_size; - result = fetch_block(fd, block); - if (result != 0) return result; - - // Two cases: - // - // - the read request is entirely within this block. In this - // case we can reply immediately. - // - // - the read request goes over into the next block. Note that - // since we mount the filesystem with max_read=block_size, a - // read can never span more than two blocks. In this case we - // copy the block to extra_block and issue a fetch for the - // following block. - - uint32_t block_offset = offset - (block * fd->block_size); - - if (size + block_offset <= fd->block_size) { - // First case: the read fits entirely in the first block. - - vec[1].iov_base = fd->block_data + block_offset; - vec[1].iov_len = size; - vec_used = 2; - } else { - // Second case: the read spills over into the next block. - - memcpy(fd->extra_block, fd->block_data + block_offset, - fd->block_size - block_offset); - vec[1].iov_base = fd->extra_block; - vec[1].iov_len = fd->block_size - block_offset; - - result = fetch_block(fd, block+1); - if (result != 0) return result; - vec[2].iov_base = fd->block_data; - vec[2].iov_len = size - vec[1].iov_len; - vec_used = 3; - } - - if (writev(fd->ffd, vec, vec_used) < 0) { - printf("*** READ REPLY FAILED: %s ***\n", strerror(errno)); - } - return NO_STATUS; -} - -int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, - uint64_t file_size, uint32_t block_size) -{ - int result; - - // If something's already mounted on our mountpoint, try to remove - // it. (Mostly in case of a previous abnormal exit.) - umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE); - - if (block_size < 1024) { - fprintf(stderr, "block size (%u) is too small\n", block_size); - return -1; - } - if (block_size > (1<<22)) { // 4 MiB - fprintf(stderr, "block size (%u) is too large\n", block_size); - return -1; - } - - struct fuse_data fd; - memset(&fd, 0, sizeof(fd)); - fd.vtab = vtab; - fd.cookie = cookie; - fd.file_size = file_size; - fd.block_size = block_size; - fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1); - - if (fd.file_blocks > (1<<18)) { - fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks); - result = -1; - goto done; - } - - fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_LENGTH); - if (fd.hashes == NULL) { - fprintf(stderr, "failed to allocate %d bites for hashes\n", - fd.file_blocks * SHA256_DIGEST_LENGTH); - result = -1; - goto done; - } - - fd.uid = getuid(); - fd.gid = getgid(); - - fd.curr_block = -1; - fd.block_data = (uint8_t*)malloc(block_size); - if (fd.block_data == NULL) { - fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size); - result = -1; - goto done; - } - fd.extra_block = (uint8_t*)malloc(block_size); - if (fd.extra_block == NULL) { - fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size); - result = -1; - goto done; - } - - fd.ffd = open("/dev/fuse", O_RDWR); - if (fd.ffd < 0) { - perror("open /dev/fuse"); - result = -1; - goto done; - } - - char opts[256]; - snprintf(opts, sizeof(opts), - ("fd=%d,user_id=%d,group_id=%d,max_read=%u," - "allow_other,rootmode=040000"), - fd.ffd, fd.uid, fd.gid, block_size); - - result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT, - "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts); - if (result < 0) { - perror("mount"); - goto done; - } - uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8]; - for (;;) { - ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer))); - if (len == -1) { - perror("read request"); - if (errno == ENODEV) { - result = -1; - break; - } - continue; - } - - if ((size_t)len < sizeof(struct fuse_in_header)) { - fprintf(stderr, "request too short: len=%zu\n", (size_t)len); - continue; - } - - struct fuse_in_header* hdr = (struct fuse_in_header*) request_buffer; - void* data = request_buffer + sizeof(struct fuse_in_header); - - result = -ENOSYS; - - switch (hdr->opcode) { - case FUSE_INIT: - result = handle_init(data, &fd, hdr); - break; - - case FUSE_LOOKUP: - result = handle_lookup(data, &fd, hdr); - break; - - case FUSE_GETATTR: - result = handle_getattr(data, &fd, hdr); - break; - - case FUSE_OPEN: - result = handle_open(data, &fd, hdr); - break; - - case FUSE_READ: - result = handle_read(data, &fd, hdr); - break; - - case FUSE_FLUSH: - result = handle_flush(data, &fd, hdr); - break; - - case FUSE_RELEASE: - result = handle_release(data, &fd, hdr); - break; - - default: - fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode); - break; - } - - if (result == NO_STATUS_EXIT) { - result = 0; - break; - } - - if (result != NO_STATUS) { - struct fuse_out_header outhdr; - outhdr.len = sizeof(outhdr); - outhdr.error = result; - outhdr.unique = hdr->unique; - TEMP_FAILURE_RETRY(write(fd.ffd, &outhdr, sizeof(outhdr))); - } - } - - done: - fd.vtab->close(fd.cookie); - - result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH); - if (result < 0) { - printf("fuse_sideload umount failed: %s\n", strerror(errno)); - } - - if (fd.ffd) close(fd.ffd); - free(fd.hashes); - free(fd.block_data); - free(fd.extra_block); - - return result; -} - -extern "C" int run_old_fuse_sideload(struct provider_vtab* vtab, void* cookie, - uint64_t file_size, uint32_t block_size) -{ - return run_fuse_sideload(vtab, cookie, file_size, block_size); -} diff --git a/fuse_sideload22.h b/fuse_sideload22.h deleted file mode 100644 index 98254d46b9..0000000000 --- a/fuse_sideload22.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 __FUSE_SIDELOAD22_H -#define __FUSE_SIDELOAD22_H - -// define the filenames created by the sideload FUSE filesystem -#define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload" -#define FUSE_SIDELOAD_HOST_FILENAME "package.zip" -#define FUSE_SIDELOAD_HOST_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_FILENAME) -#define FUSE_SIDELOAD_HOST_EXIT_FLAG "exit" -#define FUSE_SIDELOAD_HOST_EXIT_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_EXIT_FLAG) - -struct provider_vtab { - // read a block - int (*read_block)(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size); - - // close down - void (*close)(void* cookie); -}; - -int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, - uint64_t file_size, uint32_t block_size); - -#ifdef __cplusplus -extern "C" { -#endif -int run_old_fuse_sideload(struct provider_vtab* vtab, void* cookie, - uint64_t file_size, uint32_t block_size); -#ifdef __cplusplus -} -#endif - -#endif diff --git a/gui/Android.mk b/gui/Android.mk old mode 100644 new mode 100755 index 22b5347702..d9cae7ff7e --- a/gui/Android.mk +++ b/gui/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_CFLAGS := -fno-strict-aliasing +LOCAL_CFLAGS := -fno-strict-aliasing -Wno-implicit-fallthrough LOCAL_SRC_FILES := \ gui.cpp \ @@ -32,6 +32,10 @@ LOCAL_SRC_FILES := \ terminal.cpp \ twmsg.cpp +ifneq ($(TW_DELAY_TOUCH_INIT_MS),) + LOCAL_CFLAGS += -DTW_DELAY_TOUCH_INIT_MS=$(TW_DELAY_TOUCH_INIT_MS) +endif + ifneq ($(TWRP_CUSTOM_KEYBOARD),) LOCAL_SRC_FILES += $(TWRP_CUSTOM_KEYBOARD) else @@ -39,13 +43,38 @@ else endif LOCAL_SHARED_LIBRARIES += libminuitwrp libc libstdc++ libaosprecovery libselinux +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../otautil/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/../twrpinstall/include ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) LOCAL_SHARED_LIBRARIES += libziparchive - LOCAL_C_INCLUDES += $(LOCAL_PATH)/../otautil/include + LOCAL_STATIC_LIBRARIES += libotautil libtwrpinstall + 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/ \ + $(LOCAL_PATH)/../recovery_ui/include \ + $(LOCAL_PATH)/../fuse_sideload/include + LOCAL_CFLAGS += -D_USE_SYSTEM_ZIPARCHIVE + else + LOCAL_C_INCLUDES += $(LOCAL_PATH)/../install28/ \ + $(LOCAL_PATH)/../fuse_sideload28/ + LOCAL_CFLAGS += -DUSE_28_INSTALL -DUSE_OTAUTIL_ZIPARCHIVE + endif else LOCAL_SHARED_LIBRARIES += libminzip LOCAL_CFLAGS += -DUSE_MINZIP endif +ifeq ($(TARGET_USERIMAGES_USE_EXT4), true) + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -le 28; echo $$?),0) + LOCAL_C_INCLUDES += system/extras/ext4_utils \ + system/extras/ext4_utils/include \ + $(LOCAL_PATH)/../crypto/ext4crypt + LOCAL_SHARED_LIBRARIES += libext4_utils + endif +endif + LOCAL_MODULE := libguitwrp #TWRP_EVENT_LOGGING := true @@ -55,7 +84,11 @@ endif ifneq ($(TW_USE_KEY_CODE_TOUCH_SYNC),) LOCAL_CFLAGS += -DTW_USE_KEY_CODE_TOUCH_SYNC=$(TW_USE_KEY_CODE_TOUCH_SYNC) endif - +ifneq ($(TW_OZIP_DECRYPT_KEY),) + LOCAL_CFLAGS += -DTW_OZIP_DECRYPT_KEY=\"$(TW_OZIP_DECRYPT_KEY)\" +else + LOCAL_CFLAGS += -DTW_OZIP_DECRYPT_KEY=0 +endif ifneq ($(TW_NO_SCREEN_BLANK),) LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK endif @@ -80,11 +113,22 @@ endif ifeq ($(TW_ROUND_SCREEN), true) LOCAL_CFLAGS += -DTW_ROUND_SCREEN endif +ifeq ($(TW_EXCLUDE_NANO), true) + LOCAL_CFLAGS += -DTW_EXCLUDE_NANO +endif +ifeq ($(AB_OTA_UPDATER),true) + LOCAL_CFLAGS += -DAB_OTA_UPDATER=1 +endif +ifeq ($(TW_SCREEN_BLANK_ON_BOOT), true) + LOCAL_CFLAGS += -DTW_SCREEN_BLANK_ON_BOOT +endif LOCAL_C_INCLUDES += \ bionic \ + system/core/base/include \ system/core/include \ - system/core/libpixelflinger/include + system/core/libpixelflinger/include \ + external/freetype/include ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) LOCAL_C_INCLUDES += external/stlport/stlport @@ -98,7 +142,7 @@ include $(BUILD_STATIC_LIBRARY) # Transfer in the resources for the device include $(CLEAR_VARS) LOCAL_MODULE := twrp -LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)$(TWRES_PATH) diff --git a/gui/action.cpp b/gui/action.cpp index 0216d849e2..87e9f30ca6 100755 --- a/gui/action.cpp +++ b/gui/action.cpp @@ -33,36 +33,41 @@ #include #include #include +#include #include #include #include "../partitions.hpp" #include "../twrp-functions.hpp" +#include "../twrpRepacker.hpp" #include "../openrecoveryscript.hpp" -#include "../adb_install.h" -#include "../fuse_sideload.h" +#include "twinstall/adb_install.h" + +#include "fuse_sideload.h" #include "blanktimer.hpp" -#include "../twinstall.h" +#include "twinstall.h" extern "C" { #include "../twcommon.h" #include "../variables.h" #include "cutils/properties.h" -#include "../adb_install.h" +#include "install/adb_install.h" }; -#include "../set_metadata.h" +#include "set_metadata.h" #include "../minuitwrp/minui.h" #include "rapidxml.hpp" #include "objects.hpp" -#include "../tw_atomic.hpp" +#include "tw_atomic.hpp" GUIAction::mapFunc GUIAction::mf; std::set GUIAction::setActionsRunningInCallerThread; static string zip_queue[10]; static int zip_queue_index; pid_t sideload_child_pid; +extern std::vector Users_List; +extern GUITerminal* term; static void *ActionThread_work_wrapper(void *data); @@ -199,6 +204,9 @@ GUIAction::GUIAction(xml_node<>* node) ADD_ACTION(setlanguage); ADD_ACTION(checkforapp); ADD_ACTION(togglebacklight); + ADD_ACTION(enableadb); + ADD_ACTION(enablefastboot); + ADD_ACTION(changeterminal); // remember actions that run in the caller thread for (mapFunc::const_iterator it = mf.begin(); it != mf.end(); ++it) @@ -234,6 +242,10 @@ GUIAction::GUIAction(xml_node<>* node) ADD_ACTION(uninstalltwrpsystemapp); ADD_ACTION(repackimage); ADD_ACTION(fixabrecoverybootloop); + ADD_ACTION(applycustomtwrpfolder); +#ifndef TW_EXCLUDE_NANO + ADD_ACTION(editfile); +#endif } // First, get the action @@ -369,6 +381,8 @@ int GUIAction::flash_zip(std::string filename, int* wipe_cache) int ret_val = 0; DataManager::SetValue("ui_progress", 0); + DataManager::SetValue("ui_portion_size", 0); + DataManager::SetValue("ui_portion_start", 0); if (filename.empty()) { @@ -389,16 +403,16 @@ int GUIAction::flash_zip(std::string filename, int* wipe_cache) if (simulate) { simulate_progress_bar(); } else { - ret_val = TWinstall_zip(filename.c_str(), wipe_cache); - + ret_val = TWinstall_zip(filename.c_str(), wipe_cache, (bool) !DataManager::GetIntValue(TW_SKIP_DIGEST_CHECK_ZIP_VAR)); + PartitionManager.Unlock_Block_Partitions(); // Now, check if we need to ensure TWRP remains installed... struct stat st; - if (stat("/sbin/installTwrp", &st) == 0) + if (stat("/system/bin/installTwrp", &st) == 0) { DataManager::SetValue("tw_operation", "Configuring TWRP"); DataManager::SetValue("tw_partition", ""); gui_msg("config_twrp=Configuring TWRP..."); - if (TWFunc::Exec_Cmd("/sbin/installTwrp reinstall") < 0) + if (TWFunc::Exec_Cmd("/system/bin/installTwrp reinstall") < 0) { gui_msg("config_twrp_err=Unable to configure TWRP with this kernel."); } @@ -408,6 +422,8 @@ int GUIAction::flash_zip(std::string filename, int* wipe_cache) // Done DataManager::SetValue("ui_progress", 100); DataManager::SetValue("ui_progress", 0); + DataManager::SetValue("ui_portion_size", 0); + DataManager::SetValue("ui_portion_start", 0); return ret_val; } @@ -490,10 +506,12 @@ void GUIAction::operation_start(const string operation_name) time(&Start); DataManager::SetValue(TW_ACTION_BUSY, 1); DataManager::SetValue("ui_progress", 0); + DataManager::SetValue("ui_portion_size", 0); + DataManager::SetValue("ui_portion_start", 0); DataManager::SetValue("tw_operation", operation_name); DataManager::SetValue("tw_operation_state", 0); DataManager::SetValue("tw_operation_status", 0); - bool tw_ab_device = TWFunc::get_cache_dir() != NON_AB_CACHE_DIR; + bool tw_ab_device = TWFunc::get_log_dir() != CACHE_LOGS_DIR; DataManager::SetValue("tw_ab_device", tw_ab_device); } @@ -914,19 +932,19 @@ int GUIAction::getpartitiondetails(std::string arg) DataManager::SetValue("tw_partition_can_resize", 1); else DataManager::SetValue("tw_partition_can_resize", 0); - if (TWFunc::Path_Exists("/sbin/mkfs.fat")) + if (TWFunc::Path_Exists("/system/bin/mkfs.fat")) DataManager::SetValue("tw_partition_vfat", 1); else DataManager::SetValue("tw_partition_vfat", 0); - if (TWFunc::Path_Exists("/sbin/mkexfatfs")) + if (TWFunc::Path_Exists("/system/bin/mkexfatfs")) DataManager::SetValue("tw_partition_exfat", 1); else DataManager::SetValue("tw_partition_exfat", 0); - if (TWFunc::Path_Exists("/sbin/mkfs.f2fs")) + if (TWFunc::Path_Exists("/system/bin/mkfs.f2fs") || TWFunc::Path_Exists("/system/bin/make_f2fs")) DataManager::SetValue("tw_partition_f2fs", 1); else DataManager::SetValue("tw_partition_f2fs", 0); - if (TWFunc::Path_Exists("/sbin/mke2fs")) + if (TWFunc::Path_Exists("/system/bin/mke2fs")) DataManager::SetValue("tw_partition_ext", 1); else DataManager::SetValue("tw_partition_ext", 0); @@ -1022,6 +1040,17 @@ void GUIAction::reinject_after_flash() } } +int GUIAction::ozip_decrypt(string zip_path) +{ + if (!TWFunc::Path_Exists("/system/bin/ozip_decrypt")) { + return 1; + } + gui_msg("ozip_decrypt_decryption=Starting Ozip Decryption..."); + TWFunc::Exec_Cmd("ozip_decrypt " + (string)TW_OZIP_DECRYPT_KEY + " '" + zip_path + "'"); + gui_msg("ozip_decrypt_finish=Ozip Decryption Finished!"); + return 0; +} + int GUIAction::flash(std::string arg) { int i, ret_val = 0, wipe_cache = 0; @@ -1032,6 +1061,20 @@ int GUIAction::flash(std::string arg) size_t slashpos = zip_path.find_last_of('/'); string zip_filename = (slashpos == string::npos) ? zip_path : zip_path.substr(slashpos + 1); operation_start("Flashing"); + if((zip_path.substr(zip_path.size() - 4, 4)) == "ozip") + { + if((ozip_decrypt(zip_path)) != 0) + { + LOGERR("Unable to find ozip_decrypt!"); + break; + } + zip_filename = (zip_filename.substr(0, zip_filename.size() - 4)).append("zip"); + zip_path = (zip_path.substr(0, zip_path.size() - 4)).append("zip"); + if (!TWFunc::Path_Exists(zip_path)) { + LOGERR("Unable to find decrypted zip"); + break; + } + } DataManager::SetValue("tw_filename", zip_path); DataManager::SetValue("tw_file", zip_filename); DataManager::SetValue(TW_ZIP_INDEX, (i + 1)); @@ -1495,12 +1538,31 @@ int GUIAction::decrypt(std::string arg __unused) simulate_progress_bar(); } else { string Password; + string userID; DataManager::GetValue("tw_crypto_password", Password); - op_status = PartitionManager.Decrypt_Device(Password); + + if (DataManager::GetIntValue(TW_IS_FBE)) { // for FBE + DataManager::GetValue("tw_crypto_user_id", userID); + if (userID != "") { + op_status = PartitionManager.Decrypt_Device(Password, atoi(userID.c_str())); + if (userID != "0") { + if (op_status != 0) + op_status = 1; + operation_end(op_status); + return 0; + } + } else { + LOGINFO("User ID not found\n"); + op_status = 1; + } + ::sleep(1); + } else { // for FDE + op_status = PartitionManager.Decrypt_Device(Password); + } + if (op_status != 0) op_status = 1; else { - DataManager::SetValue(TW_IS_ENCRYPTED, 0); int has_datamedia; @@ -1533,7 +1595,9 @@ int GUIAction::adbsideload(std::string arg __unused) bool mtp_was_enabled = TWFunc::Toggle_MTP(false); // wait for the adb connection - int ret = apply_from_adb("/", &sideload_child_pid); + Device::BuiltinAction reboot_action = Device::REBOOT_BOOTLOADER; + int ret = twrp_sideload("/", &reboot_action); + sideload_child_pid = GetMiniAdbdPid(); DataManager::SetValue("tw_has_cancel", 0); // Remove cancel button from gui now that the zip install is going to start if (ret != 0) { @@ -1544,27 +1608,11 @@ int GUIAction::adbsideload(std::string arg __unused) int wipe_cache = 0; int wipe_dalvik = 0; DataManager::GetValue("tw_wipe_dalvik", wipe_dalvik); - - if (TWinstall_zip(FUSE_SIDELOAD_HOST_PATHNAME, &wipe_cache) == 0) { - if (wipe_cache || DataManager::GetIntValue("tw_wipe_cache")) - PartitionManager.Wipe_By_Path("/cache"); - if (wipe_dalvik) - PartitionManager.Wipe_Dalvik_Cache(); - } else { - ret = 1; // failure - } - } - if (sideload_child_pid) { - LOGINFO("Signaling child sideload process to exit.\n"); - struct stat st; - // Calling stat() on this magic filename signals the minadbd - // subprocess to shut down. - stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); - int status; - LOGINFO("Waiting for child sideload process to exit.\n"); - waitpid(sideload_child_pid, &status, 0); + if (wipe_cache || DataManager::GetIntValue("tw_wipe_cache")) + PartitionManager.Wipe_By_Path("/cache"); + if (wipe_dalvik) + PartitionManager.Wipe_Dalvik_Cache(); } - property_set("ctl.start", "adbd"); TWFunc::Toggle_MTP(mtp_was_enabled); reinject_after_flash(); operation_end(ret); @@ -1581,6 +1629,7 @@ int GUIAction::adbsideloadcancel(std::string arg __unused) // Calling stat() on this magic filename signals the minadbd // subprocess to shut down. stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); + sideload_child_pid = GetMiniAdbdPid(); if (!sideload_child_pid) { LOGERR("Unable to get child ID\n"); return 0; @@ -1760,16 +1809,41 @@ int GUIAction::stopmtp(std::string arg __unused) int GUIAction::flashimage(std::string arg __unused) { int op_status = 0; + bool flag = true; operation_start("Flash Image"); string path, filename; DataManager::GetValue("tw_zip_location", path); DataManager::GetValue("tw_file", filename); - if (PartitionManager.Flash_Image(path, filename)) - op_status = 0; // success - else - op_status = 1; // fail +#ifdef AB_OTA_UPDATER + string target = DataManager::GetStrValue("tw_flash_partition"); + unsigned int pos = target.find_last_of(';'); + string mount_point = pos != string::npos ? target.substr(0, pos) : ""; + TWPartition* t_part = PartitionManager.Find_Partition_By_Path(mount_point); + bool flash_in_both_slots = DataManager::GetIntValue("tw_flash_both_slots") ? true : false; + + if (t_part != NULL && (flash_in_both_slots && t_part->SlotSelect)) + { + string current_slot = PartitionManager.Get_Active_Slot_Display(); + bool pre_op_status = PartitionManager.Flash_Image(path, filename); + + PartitionManager.Set_Active_Slot(current_slot == "A" ? "B" : "A"); + op_status = (int) !(pre_op_status && PartitionManager.Flash_Image(path, filename)); + PartitionManager.Set_Active_Slot(current_slot); + + DataManager::SetValue("tw_flash_both_slots", 0); + flag = false; + } +#endif + if (flag) + { + if (PartitionManager.Flash_Image(path, filename)) + op_status = 0; // success + else + op_status = 1; // fail + } + operation_end(op_status); return 0; } @@ -1894,10 +1968,19 @@ int GUIAction::togglebacklight(std::string arg __unused) int GUIAction::setbootslot(std::string arg) { operation_start("Set Boot Slot"); - if (!simulate) - PartitionManager.Set_Active_Slot(arg); - else + if (!simulate) { + if (!PartitionManager.UnMount_By_Path("/vendor", false)) { + // PartitionManager failed to unmount /vendor, this should not happen, + // but in case it does, do a lazy unmount + LOGINFO("WARNING: vendor partition could not be unmounted normally!\n"); + umount2("/vendor", MNT_DETACH); + PartitionManager.Set_Active_Slot(arg); + } else { + PartitionManager.Set_Active_Slot(arg); + } + } else { simulate_progress_bar(); + } operation_end(0); return 0; } @@ -1907,48 +1990,10 @@ int GUIAction::checkforapp(std::string arg __unused) operation_start("Check for TWRP App"); if (!simulate) { - string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk"); - int sdkver = 0; - if (!sdkverstr.empty()) { - sdkver = atoi(sdkverstr.c_str()); - } - if (sdkver <= 13) { - if (sdkver == 0) - LOGINFO("Unable to read sdk version from build prop\n"); - else - LOGINFO("SDK version too low for TWRP app (%i < 14)\n", sdkver); - DataManager::SetValue("tw_app_install_status", 1); // 0 = no status, 1 = not installed, 2 = already installed or do not install - goto exit; - } - if (TWFunc::Is_TWRP_App_In_System()) { - DataManager::SetValue("tw_app_install_status", 2); // 0 = no status, 1 = not installed, 2 = already installed or do not install - goto exit; - } - if (PartitionManager.Mount_By_Path("/data", false)) { - const char parent_path[] = "/data/app"; - const char app_prefix[] = "me.twrp.twrpapp-"; - DIR *d = opendir(parent_path); - if (d) { - struct dirent *p; - while ((p = readdir(d))) { - if (p->d_type != DT_DIR || strlen(p->d_name) < strlen(app_prefix) || strncmp(p->d_name, app_prefix, strlen(app_prefix))) - continue; - closedir(d); - LOGINFO("App found at '%s/%s'\n", parent_path, p->d_name); - DataManager::SetValue("tw_app_install_status", 2); // 0 = no status, 1 = not installed, 2 = already installed or do not install - goto exit; - } - closedir(d); - } - } else { - LOGINFO("Data partition cannot be mounted during app check\n"); - DataManager::SetValue("tw_app_install_status", 2); // 0 = no status, 1 = not installed, 2 = already installed or do not install - } + TWFunc::checkforapp(); } else simulate_progress_bar(); - LOGINFO("App not installed\n"); - DataManager::SetValue("tw_app_install_status", 1); // 0 = no status, 1 = not installed, 2 = already installed -exit: + operation_end(0); return 0; } @@ -1991,7 +2036,7 @@ int GUIAction::installapp(std::string arg __unused) goto exit; } install_path += "/base.apk"; - if (TWFunc::copy_file("/sbin/me.twrp.twrpapp.apk", install_path, 0644)) { + if (TWFunc::copy_file("/system/bin/me.twrp.twrpapp.apk", install_path, 0644)) { LOGERR("Error copying apk file\n"); goto exit; } @@ -2024,7 +2069,7 @@ int GUIAction::installapp(std::string arg __unused) goto exit; } install_path += "/me.twrp.twrpapp.apk"; - if (TWFunc::copy_file("/sbin/me.twrp.twrpapp.apk", install_path, 0644)) { + if (TWFunc::copy_file("/system/bin/me.twrp.twrpapp.apk", install_path, 0644)) { LOGERR("Error copying apk file\n"); goto exit; } @@ -2032,6 +2077,22 @@ int GUIAction::installapp(std::string arg __unused) LOGERR("setfilecon %s error: %s\n", install_path.c_str(), strerror(errno)); goto exit; } + + // System apps require their permissions to be pre-set via an XML file in /etc/permissions + string permission_path = base_path + "/etc/permissions/privapp-permissions-twrpapp.xml"; + if (TWFunc::copy_file("/system/bin/privapp-permissions-twrpapp.xml", permission_path, 0644)) { + LOGERR("Error copying permission file\n"); + goto exit; + } + if (chown(permission_path.c_str(), 1000, 1000)) { + LOGERR("chown %s error: %s\n", permission_path.c_str(), strerror(errno)); + goto exit; + } + if (setfilecon(permission_path.c_str(), (security_context_t)context.c_str()) < 0) { + LOGERR("setfilecon %s error: %s\n", permission_path.c_str(), strerror(errno)); + goto exit; + } + sync(); sync(); PartitionManager.UnMount_By_Path(PartitionManager.Get_Android_Root_Path(), true); @@ -2045,6 +2106,7 @@ int GUIAction::installapp(std::string arg __unused) } else simulate_progress_bar(); exit: + TWFunc::checkforapp(); operation_end(0); return 0; } @@ -2098,6 +2160,7 @@ int GUIAction::uninstalltwrpsystemapp(std::string arg __unused) } else simulate_progress_bar(); exit: + TWFunc::checkforapp(); operation_end(0); return 0; } @@ -2105,6 +2168,8 @@ int GUIAction::uninstalltwrpsystemapp(std::string arg __unused) int GUIAction::repackimage(std::string arg __unused) { int op_status = 1; + twrpRepacker repacker; + operation_start("Repack Image"); if (!simulate) { @@ -2117,7 +2182,7 @@ int GUIAction::repackimage(std::string arg __unused) Repack_Options.Type = REPLACE_KERNEL; else Repack_Options.Type = REPLACE_RAMDISK; - if (!PartitionManager.Repack_Images(path, Repack_Options)) + if (!repacker.Repack_Image_And_Flash(path, Repack_Options)) goto exit; } else simulate_progress_bar(); @@ -2130,10 +2195,12 @@ int GUIAction::repackimage(std::string arg __unused) int GUIAction::fixabrecoverybootloop(std::string arg __unused) { int op_status = 1; + twrpRepacker repacker; + operation_start("Repack Image"); if (!simulate) { - if (!TWFunc::Path_Exists("/sbin/magiskboot")) { + if (!TWFunc::Path_Exists("/system/bin/magiskboot")) { LOGERR("Image repacking tool not present in this TWRP build!"); goto exit; } @@ -2145,11 +2212,11 @@ int GUIAction::fixabrecoverybootloop(std::string arg __unused) gui_msg(Msg(msg::kError, "unable_to_locate=Unable to locate {1}.")("/boot")); goto exit; } - if (!PartitionManager.Prepare_Repack(part, REPACK_ORIG_DIR, DataManager::GetIntValue("tw_repack_backup_first") != 0, gui_lookup("repack", "Repack"))) + if (!repacker.Backup_Image_For_Repack(part, REPACK_ORIG_DIR, DataManager::GetIntValue("tw_repack_backup_first") != 0, gui_lookup("repack", "Repack"))) goto exit; DataManager::SetProgress(.25); gui_msg("fixing_recovery_loop_patch=Patching kernel..."); - std::string command = "cd " REPACK_ORIG_DIR " && /sbin/magiskboot --hexpatch kernel 77616E745F696E697472616D667300 736B69705F696E697472616D667300"; + std::string command = "cd " REPACK_ORIG_DIR " && /system/bin/magiskboot hexpatch kernel 77616E745F696E697472616D667300 736B69705F696E697472616D667300"; if (TWFunc::Exec_Cmd(command) != 0) { gui_msg(Msg(msg::kError, "fix_recovery_loop_patch_error=Error patching kernel.")); goto exit; @@ -2165,7 +2232,7 @@ int GUIAction::fixabrecoverybootloop(std::string arg __unused) } DataManager::SetProgress(.5); gui_msg(Msg("repacking_image=Repacking {1}...")(part->Display_Name)); - command = "cd " REPACK_ORIG_DIR " && /sbin/magiskboot --repack " REPACK_ORIG_DIR "boot.img"; + command = "cd " REPACK_ORIG_DIR " && /system/bin/magiskboot repack " REPACK_ORIG_DIR "boot.img"; if (TWFunc::Exec_Cmd(command) != 0) { gui_msg(Msg(msg::kError, "repack_error=Error repacking image.")); goto exit; @@ -2187,3 +2254,94 @@ int GUIAction::fixabrecoverybootloop(std::string arg __unused) operation_end(op_status); return 0; } + + +int GUIAction::enableadb(std::string arg __unused) { + android::base::SetProperty("sys.usb.config", "none"); + android::base::SetProperty("sys.usb.config", "adb"); + return 0; +} + +int GUIAction::enablefastboot(std::string arg __unused) { + android::base::SetProperty("sys.usb.config", "none"); + android::base::SetProperty("sys.usb.config", "fastboot"); + return 0; +} + +int GUIAction::changeterminal(std::string arg) { + bool res = true; + std::string resp, cmd = "cd " + arg; + DataManager::GetValue("tw_terminal_location", resp); + if (arg.empty() && !resp.empty()) { + cmd = "cd /"; + for (uint8_t iter = 0; iter < cmd.size(); iter++) + term->NotifyCharInput(cmd.at(iter)); + term->NotifyCharInput(13); + DataManager::SetValue("tw_terminal_location", ""); + return 0; + } + if (term != NULL && !arg.empty()) { + DataManager::SetValue("tw_terminal_location", arg); + if (term->status()) { + for (uint8_t iter = 0; iter < cmd.size(); iter++) + term->NotifyCharInput(cmd.at(iter)); + term->NotifyCharInput(13); + } + else if (chdir(arg.c_str()) != 0) { + LOGINFO("Unable to change dir to %s\n", arg.c_str()); + res = false; + } + } + else { + res = false; + LOGINFO("Unable to switch to Terminal\n"); + } + if (res) + gui_changePage("terminalcommand"); + return 0; +} +#ifndef TW_EXCLUDE_NANO +int GUIAction::editfile(std::string arg) { + if (term != NULL) { + for (uint8_t iter = 0; iter < arg.size(); iter++) + term->NotifyCharInput(arg.at(iter)); + term->NotifyCharInput(13); + } + else + LOGINFO("Unable to switch to Terminal\n"); + return 0; +} +#endif + +int GUIAction::applycustomtwrpfolder(string arg __unused) +{ + operation_start("ChangingTWRPFolder"); + string storageFolder = DataManager::GetSettingsStoragePath(); + string newFolder = storageFolder + '/' + arg; + string newBackupFolder = newFolder + "/BACKUPS/" + DataManager::GetStrValue("device_id"); + string prevFolder = storageFolder + DataManager::GetStrValue(TW_RECOVERY_FOLDER_VAR); + bool ret = false; + + if (TWFunc::Path_Exists(newFolder)) { + gui_msg(Msg(msg::kError, "tw_folder_exists=A folder with that name already exists!")); + } else { + ret = true; + } + + if (newFolder != prevFolder && ret) { + ret = TWFunc::Exec_Cmd("mv -f \"" + prevFolder + "\" \"" + newFolder + '\"') != 0 ? false : true; + } else { + gui_msg(Msg(msg::kError, "tw_folder_exists=A folder with that name already exists!")); + } + + if (ret) ret = TWFunc::Recursive_Mkdir(newBackupFolder) ? true : false; + + + if (ret) { + DataManager::SetValue(TW_RECOVERY_FOLDER_VAR, '/' + arg); + DataManager::SetValue(TW_BACKUPS_FOLDER_VAR, newBackupFolder); + DataManager::mBackingFile = newFolder + '/' + TW_SETTINGS_FILE; + } + operation_end((int)!ret); + return 0; +} diff --git a/gui/blanktimer.cpp b/gui/blanktimer.cpp old mode 100644 new mode 100755 index 63cd05c5c4..a9573ad5ac --- a/gui/blanktimer.cpp +++ b/gui/blanktimer.cpp @@ -66,7 +66,7 @@ void blanktimer::checkForTimeout() { if (sleepTimer && diff.tv_sec > sleepTimer && state < kOff) { state = kOff; TWFunc::Set_Brightness("0"); - TWFunc::check_and_run_script("/sbin/postscreenblank.sh", "blank"); + TWFunc::check_and_run_script("/system/bin/postscreenblank.sh", "blank"); PageManager::ChangeOverlay("lock"); } #ifndef TW_NO_SCREEN_BLANK @@ -100,7 +100,7 @@ void blanktimer::resetTimerAndUnblank(void) { gr_fb_blank(false); #endif // TODO: this is asymmetric with postscreenblank.sh - shouldn't it be under the next case label? - TWFunc::check_and_run_script("/sbin/postscreenunblank.sh", "unblank"); + TWFunc::check_and_run_script("/system/bin/postscreenunblank.sh", "unblank"); // No break here, we want to keep going case kOff: gui_forceRender(); @@ -129,7 +129,7 @@ void blanktimer::blank(void) { orig_brightness = getBrightness(); state = kOff; TWFunc::Set_Brightness("0"); - TWFunc::check_and_run_script("/sbin/postscreenblank.sh", "blank"); + TWFunc::check_and_run_script("/system/bin/postscreenblank.sh", "blank"); } #ifndef TW_NO_SCREEN_BLANK if (state == kOff) { diff --git a/gui/blanktimer.hpp b/gui/blanktimer.hpp index fe7b77c174..6aca270878 100644 --- a/gui/blanktimer.hpp +++ b/gui/blanktimer.hpp @@ -42,10 +42,11 @@ class blanktimer bool isScreenOff(); + void blank(void); + private: void setTimer(void); string getBrightness(void); - void blank(void); pthread_mutex_t mutex; enum State { kOn = 0, kDim = 1, kOff = 2, kBlanked = 3 }; diff --git a/gui/console.cpp b/gui/console.cpp old mode 100644 new mode 100755 index 03628ec308..77fdd96790 --- a/gui/console.cpp +++ b/gui/console.cpp @@ -1,5 +1,5 @@ /* - Copyright 2015 bigbiff/Dees_Troy TeamWin + Copyright 2012 - 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify diff --git a/gui/fileselector.cpp b/gui/fileselector.cpp index fe378c8482..a32743a584 100644 --- a/gui/fileselector.cpp +++ b/gui/fileselector.cpp @@ -20,7 +20,15 @@ #include #include #include - +#ifdef __ANDROID_API_M__ +#include +#ifdef __ANDROID_API_N__ +#include +#else +#include +#endif +#else +#endif extern "C" { #include "../twcommon.h" } @@ -61,6 +69,12 @@ GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIScrollList(node) if (attr) mShowNavFolders = atoi(attr->value()); } + child = FindNode(node, "prfxfilter"); + if (child) { + attr = child->first_attribute("prfx"); + if (attr) + mPrfx = attr->value(); + } // Handle the path variable child = FindNode(node, "path"); @@ -253,6 +267,7 @@ int GUIFileSelector::GetFileList(const std::string folder) while ((de = readdir(d)) != NULL) { FileData data; + bool match = false; data.fileName = de->d_name; if (data.fileName == ".") @@ -279,11 +294,57 @@ int GUIFileSelector::GetFileList(const std::string folder) if (mShowNavFolders || (data.fileName != "." && data.fileName != "..")) mFolderList.push_back(data); } else if (data.fileType == DT_REG || data.fileType == DT_LNK || data.fileType == DT_BLK) { - if (mExtn.empty() || (data.fileName.length() > mExtn.length() && data.fileName.substr(data.fileName.length() - mExtn.length()) == mExtn)) { - if (mExtn == ".ab" && twadbbu::Check_ADB_Backup_File(path)) +#ifdef __ANDROID_API_M__ + std::vector mExtnResults = android::base::Split(mExtn, ";"); + for (const std::string& mExtnElement : mExtnResults) + { + std::string mExtnName = android::base::Trim(mExtnElement); + if (mExtnName.empty() || (data.fileName.length() >= mExtnName.length() && data.fileName.substr(data.fileName.length() - mExtnName.length()) == mExtnName)) { + if (mExtnName == ".ab" && twadbbu::Check_ADB_Backup_File(path)) + mFolderList.push_back(data); + else + mFileList.push_back(data); + match = true; + break; + } + } + + if (!match) { + std::vector mPrfxResults = android::base::Split(mPrfx, ";"); + for (const std::string& mPrfxElement : mPrfxResults) + { + std::string mPrfxName = android::base::Trim(mPrfxElement); + if (!mPrfxName.empty() && data.fileName.length() >= mPrfxName.length() && data.fileName.substr(0, mPrfxName.length()) == mPrfxName) { + mFileList.push_back(data); + } +#else //On android 5.1 we can't use android::base::Trim and Split so just use the first extension written in the list + std::size_t seppos = mExtn.find_first_of(";"); + std::string mExtnf; + if (seppos!=std::string::npos){ + mExtnf = mExtn.substr(0, seppos); + } else { + mExtnf = mExtn; + } + if (mExtnf.empty() || (data.fileName.length() >= mExtnf.length() && data.fileName.substr(data.fileName.length() - mExtnf.length()) == mExtnf)) { + if (mExtnf == ".ab" && twadbbu::Check_ADB_Backup_File(path)) mFolderList.push_back(data); else mFileList.push_back(data); + match = true; + } + + if (!match) { + std::size_t seppos = mPrfx.find_first_of(";"); + std::string mPrfxf; + if (seppos!=std::string::npos){ + mPrfxf = mPrfx.substr(0, seppos); + } else { + mPrfxf = mPrfx; + } + if (!mPrfxf.empty() && data.fileName.length() >= mPrfxf.length() && data.fileName.substr(0, mPrfxf.length()) == mPrfxf) { + mFileList.push_back(data); +#endif + } } } } diff --git a/gui/gui.cpp b/gui/gui.cpp old mode 100644 new mode 100755 index ce8c3e29d9..da35d23658 --- a/gui/gui.cpp +++ b/gui/gui.cpp @@ -50,7 +50,7 @@ extern "C" #include "../openrecoveryscript.hpp" #include "../orscmd/orscmd.h" #include "blanktimer.hpp" -#include "../tw_atomic.hpp" +#include "tw_atomic.hpp" // Enable to print render time of each frame to the log file //#define PRINT_RENDER_TIME 1 @@ -650,8 +650,9 @@ static int runPages(const char *page_name, const int stop_on_page_done) gui_changePage("main"); break; } - if (DataManager::GetIntValue("tw_gui_done") != 0) + if (DataManager::GetIntValue("tw_gui_done") != 0) { break; + } } if (ors_read_fd > 0) close(ors_read_fd); @@ -754,6 +755,12 @@ extern "C" int gui_init(void) gr_init(); TWFunc::Set_Brightness(DataManager::GetStrValue("tw_brightness")); +#ifdef TW_SCREEN_BLANK_ON_BOOT + printf("TW_SCREEN_BLANK_ON_BOOT := true\n"); + blankTimer.blank(); + blankTimer.resetTimerAndUnblank(); +#endif + // load and show splash screen if (PageManager::LoadPackage("splash", TWRES "splash.xml", "splash")) { LOGERR("Failed to load splash screen XML.\n"); @@ -765,6 +772,9 @@ extern "C" int gui_init(void) PageManager::ReleasePackage("splash"); } +#ifdef TW_DELAY_TOUCH_INIT_MS + usleep(TW_DELAY_TOUCH_INIT_MS); +#endif ev_init(); return 0; } @@ -774,7 +784,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")) @@ -858,10 +867,12 @@ extern "C" int gui_loadCustomResources(void) #endif return 0; +#ifndef TW_OEM_BUILD error: LOGERR("An internal error has occurred: unable to load theme.\n"); gGuiInitialized = 0; return -1; +#endif } extern "C" int gui_start(void) @@ -869,7 +880,7 @@ extern "C" int gui_start(void) return gui_startPage("main", 1, 0); } -extern "C" int gui_startPage(const char *page_name, const int allow_commands, int stop_on_page_done) +extern "C" int gui_startPage(const char *page_name, __attribute__((unused)) const int allow_commands, int stop_on_page_done) { if (!gGuiInitialized) return -1; diff --git a/gui/gui.hpp b/gui/gui.hpp index d5b9553d69..7e4ee4ea89 100644 --- a/gui/gui.hpp +++ b/gui/gui.hpp @@ -28,6 +28,7 @@ void gui_warn(const char* text); void gui_err(const char* text); void gui_highlight(const char* text); void gui_msg(Message msg); +void gui_err(Message msg); std::string gui_parse_text(std::string inText); std::string gui_lookup(const std::string& resource_name, const std::string& default_value); diff --git a/gui/hardwarekeyboard.cpp b/gui/hardwarekeyboard.cpp index 68be9065da..12c4737a1a 100644 --- a/gui/hardwarekeyboard.cpp +++ b/gui/hardwarekeyboard.cpp @@ -34,7 +34,7 @@ #include extern "C" { -#include "../common.h" +#include "common.h" } #include "../twcommon.h" diff --git a/gui/input.cpp b/gui/input.cpp old mode 100644 new mode 100755 index 91a1c117f8..458eb5519e --- a/gui/input.cpp +++ b/gui/input.cpp @@ -1,5 +1,5 @@ /* - Copyright 2012 to 2016 bigbiff/Dees_Troy TeamWin + Copyright 2012 to 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify @@ -41,6 +41,7 @@ extern "C" { #include "../twcommon.h" } #include "../minuitwrp/minui.h" +#include "../minuitwrp/truetype.hpp" #include "rapidxml.hpp" #include "objects.hpp" @@ -215,7 +216,7 @@ void GUIInput::UpdateDisplayText() { displayValue = mValue; } - textWidth = gr_ttf_measureEx(displayValue.c_str(), fontResource); + textWidth = twrpTruetype::gr_ttf_measureEx(displayValue.c_str(), fontResource); } void GUIInput::HandleCursorByTouch(int x) { @@ -239,7 +240,7 @@ void GUIInput::HandleCursorByTouch(int x) { for (index = 0; index <= displaySize; index++) { cursorString = displayValue.substr(0, index); - cursorX = gr_ttf_measureEx(cursorString.c_str(), fontResource) + mRenderX + scrollingX; + cursorX = twrpTruetype::gr_ttf_measureEx(cursorString.c_str(), fontResource) + mRenderX + scrollingX; if (cursorX > x) { if (index > 0 && x <= cursorX - ((x - prevX) / 2) && prevX >= mRenderX) { // This helps make sure that we can place the cursor before the very first char if the first char is @@ -280,7 +281,7 @@ void GUIInput::HandleCursorByText() { if (mCursorLocation != -1) { string cursorDisplay = displayValue; cursorDisplay.resize(mCursorLocation); - cursorTextWidth = gr_ttf_measureEx(cursorDisplay.c_str(), fontResource); + cursorTextWidth = twrpTruetype::gr_ttf_measureEx(cursorDisplay.c_str(), fontResource); } cursorX = mRenderX + cursorTextWidth + scrollingX; if (cursorX >= mRenderX + mRenderW) { diff --git a/gui/keyboard.cpp b/gui/keyboard.cpp index b6a772b017..026984c341 100755 --- a/gui/keyboard.cpp +++ b/gui/keyboard.cpp @@ -1,5 +1,5 @@ /* - Copyright 2012 bigbiff/Dees_Troy TeamWin + Copyright 2012 to 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify @@ -28,6 +28,7 @@ extern "C" { #include "gui.h" } #include "../minuitwrp/minui.h" +#include "../minuitwrp/truetype.hpp" #include "rapidxml.hpp" #include "objects.hpp" @@ -333,7 +334,7 @@ void GUIKeyboard::DrawKey(Key& key, int keyX, int keyY, int keyW, int keyH) else if (!labelText.empty() && labelFont && labelFont->GetResource()) { void* fontResource = labelFont->GetResource(); - int textW = gr_ttf_measureEx(labelText.c_str(), fontResource); + int textW = twrpTruetype::gr_ttf_measureEx(labelText.c_str(), fontResource); int textH = labelFont->GetHeight(); int textX = keyX + (keyW - textW) / 2; int textY = keyY + (keyH - textH) / 2; @@ -346,7 +347,7 @@ void GUIKeyboard::DrawKey(Key& key, int keyX, int keyY, int keyW, int keyH) void* fontResource = mLongpressFont->GetResource(); gr_color(mLongpressFontColor.red, mLongpressFontColor.green, mLongpressFontColor.blue, mLongpressFontColor.alpha); string text(1, keychar); - int textW = gr_ttf_measureEx(text.c_str(), fontResource); + int textW = twrpTruetype::gr_ttf_measureEx(text.c_str(), fontResource); int textX = keyX + keyW - longpressOffsetX - textW; int textY = keyY + longpressOffsetY; gr_textEx_scaleW(textX, textY, text.c_str(), fontResource, keyW, TOP_LEFT, 0); diff --git a/gui/listbox.cpp b/gui/listbox.cpp index 9fbe09234d..3386465e29 100644 --- a/gui/listbox.cpp +++ b/gui/listbox.cpp @@ -26,6 +26,7 @@ extern "C" { #include "rapidxml.hpp" #include "objects.hpp" #include "../data.hpp" +#include "../partitions.hpp" #include "pages.hpp" extern std::vector Language_List; @@ -82,10 +83,28 @@ GUIListBox::GUIListBox(xml_node<>* node) : GUIScrollList(node) data.selected = 0; mListItems.push_back(data); } + } else if (mVariable == "tw_crypto_user_id") { + std::vector::iterator iter; + std::vector* Users_List = PartitionManager.Get_Users_List(); + for (iter = Users_List->begin(); iter != Users_List->end(); iter++) { + if (!(*iter).isDecrypted) { + ListItem data; + data.displayName = (*iter).userName; + data.variableValue = (*iter).userId; + data.action = NULL; + DataManager::GetValue("tw_crypto_user_id", currentValue); + if (currentValue == (*iter).userId || currentValue == "") { + data.selected = 1; + DataManager::SetValue("tw_crypto_user_id", (*iter).userId); + DataManager::SetValue("tw_crypto_pwtype", (*iter).type); + } else + data.selected = 0; + mListItems.push_back(data); + } + } } - } - else - allowSelection = false; // allows using listbox as a read-only list or menu + } else + allowSelection = false; // allows using listbox as a read-only list or menu // Get the data for the list child = FindNode(node, "listitem"); @@ -94,16 +113,14 @@ GUIListBox::GUIListBox(xml_node<>* node) : GUIScrollList(node) ListItem item; attr = child->first_attribute("name"); - if (!attr) - continue; + if (!attr) continue; // We will parse display names when we get page focus to ensure that translating takes place item.displayName = attr->value(); item.variableValue = gui_parse_text(child->value()); item.selected = (child->value() == currentValue); item.action = NULL; xml_node<>* action = child->first_node("action"); - if (!action) - action = child->first_node("actions"); + if (!action) action = child->first_node("actions"); if (action) { item.action = new GUIAction(child); allowSelection = true; @@ -122,7 +139,7 @@ GUIListBox::GUIListBox(xml_node<>* node) : GUIScrollList(node) LoadConditions(child, item.mConditions); mListItems.push_back(item); - mVisibleItems.push_back(mListItems.size()-1); + mVisibleItems.push_back(mListItems.size() - 1); child = child->next_sibling("listitem"); } @@ -137,6 +154,33 @@ int GUIListBox::Update(void) if (!isConditionTrue()) return 0; + if (mVariable == "tw_crypto_user_id") { + mListItems.clear(); + std::vector::iterator iter; + std::vector* Users_List = PartitionManager.Get_Users_List(); + for (iter = Users_List->begin(); iter != Users_List->end(); iter++) { + if (!(*iter).isDecrypted) { + ListItem data; + data.displayName = (*iter).userName; + data.variableValue = (*iter).userId; + data.action = NULL; + DataManager::GetValue("tw_crypto_user_id", currentValue); + if (currentValue == (*iter).userId || currentValue == "") { + data.selected = 1; + DataManager::SetValue("tw_crypto_user_id", (*iter).userId); + DataManager::SetValue("tw_crypto_pwtype", (*iter).type); + } else + data.selected = 0; + mListItems.push_back(data); + } + } + mVisibleItems.clear(); + for (size_t i = 0; i < mListItems.size(); i++) { + mVisibleItems.push_back(i); + } + mUpdate = 1; + } + GUIScrollList::Update(); if (mUpdate) { diff --git a/gui/objects.hpp b/gui/objects.hpp old mode 100644 new mode 100755 index 3f7241831f..d4b42c297f --- a/gui/objects.hpp +++ b/gui/objects.hpp @@ -288,6 +288,7 @@ class GUIAction : public GUIObject, public ActionObject ThreadType getThreadType(const Action& action); void simulate_progress_bar(void); int flash_zip(std::string filename, int* wipe_cache); + int ozip_decrypt(std::string zip_path); void reinject_after_flash(); void operation_start(const string operation_name); void operation_end(const int operation_status); @@ -370,6 +371,13 @@ class GUIAction : public GUIObject, public ActionObject int uninstalltwrpsystemapp(std::string arg); int repackimage(std::string arg); int fixabrecoverybootloop(std::string arg); + int enableadb(std::string arg); + int enablefastboot(std::string arg); + int changeterminal(std::string arg); + int applycustomtwrpfolder(std::string arg); +#ifndef TW_EXCLUDE_NANO + int editfile(std::string arg); +#endif int simulate; }; @@ -606,6 +614,7 @@ class GUIFileSelector : public GUIScrollList std::string mPathVar; // current path displayed, saved in the data manager std::string mPathDefault; // default value for the path if none is set in mPathVar std::string mExtn; // used for filtering the file list, for example, *.zip + std::string mPrfx; // used for filtering the file list, for example, Magisk- std::string mVariable; // set when the user selects an item, pull path like /path/to/foo std::string mSortVariable; // data manager variable used to change the sorting of files std::string mSelection; // set when the user selects an item without the full path like selecting /path/to/foo would just be set to foo @@ -804,6 +813,8 @@ class GUITerminal : public GUIScrollList, public InputObject virtual size_t GetItemCount(); virtual void RenderItem(size_t itemindex, int yPos, bool selected); virtual void NotifySelect(size_t item_selected); + bool status(); + void stop(); protected: void InitAndResize(); diff --git a/gui/pages.cpp b/gui/pages.cpp old mode 100644 new mode 100755 index fd0ad9bf38..ebd2dbbf94 --- a/gui/pages.cpp +++ b/gui/pages.cpp @@ -42,14 +42,18 @@ #ifdef USE_MINZIP #include "../minzip/SysUtil.h" #else +#ifdef USE_OTAUTIL_ZIPARCHIVE #include +#else +#include +#endif #endif extern "C" { #include "../twcommon.h" #include "gui.h" } -#include "../zipwrap.hpp" +#include "zipwrap.hpp" #include "../minuitwrp/minui.h" #include "rapidxml.hpp" @@ -57,11 +61,13 @@ extern "C" { #include "blanktimer.hpp" // version 2 requires theme to handle power button as action togglebacklight -#define TW_THEME_VERSION 3 +// version 4 adds fastbootd support +#define TW_THEME_VERSION 4 #define TW_THEME_VER_ERR -2 extern int gGuiRunning; +GUITerminal* term = NULL; std::map PageManager::mPageSets; PageSet* PageManager::mCurrentSet; @@ -384,6 +390,7 @@ bool Page::ProcessNode(xml_node<>* page, std::vector*> *templates, in mRenders.push_back(element); mActions.push_back(element); mInputs.push_back(element); + term = element; } else if (type == "button") { diff --git a/gui/pages.hpp b/gui/pages.hpp old mode 100644 new mode 100755 index 282b2d5b12..8c5edafeb2 --- a/gui/pages.hpp +++ b/gui/pages.hpp @@ -21,7 +21,7 @@ #ifndef _PAGES_HEADER_HPP #define _PAGES_HEADER_HPP -#include "../zipwrap.hpp" +#include "zipwrap.hpp" #include #include #include diff --git a/gui/partitionlist.cpp b/gui/partitionlist.cpp old mode 100644 new mode 100755 index 16e09977ef..19153c506f --- 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 @@ -142,6 +142,7 @@ int GUIPartitionList::NotifyVarChange(const std::string& varName, const std::str currentValue = value; SetPosition(); } else if (ListType == "backup") { + updateList = true; MatchList(); } else if (ListType == "restore") { updateList = true; @@ -179,6 +180,8 @@ void GUIPartitionList::MatchList(void) { pos = variablelist.find(searchvalue); if (pos != string::npos) { mList.at(i).selected = 1; + TWPartition* t_part = PartitionManager.Find_Partition_By_Path(mList.at(i).Mount_Point); + DataManager::SetValue("tw_is_slot_part", t_part != NULL ? (int) t_part->SlotSelect : 0); } else { mList.at(i).selected = 0; } @@ -260,7 +263,6 @@ void GUIPartitionList::NotifySelect(size_t item_selected) } mList.at(item_selected).selected = 1; mUpdate = 1; - DataManager::SetValue(mVariable, str); } } else { @@ -272,6 +274,8 @@ void GUIPartitionList::NotifySelect(size_t item_selected) mList.at(item_selected).selected = 0; else mList.at(item_selected).selected = 1; + TWPartition* t_part = PartitionManager.Find_Partition_By_Path(mList.at(item_selected).Mount_Point); + DataManager::SetValue("tw_is_slot_part", t_part != NULL ? (int) t_part->SlotSelect : 0); int i; string variablelist; diff --git a/gui/resources.cpp b/gui/resources.cpp old mode 100644 new mode 100755 index bb2fd500a3..5efa0933d0 --- a/gui/resources.cpp +++ b/gui/resources.cpp @@ -1,5 +1,5 @@ /* - Copyright 2017 TeamWin + Copyright 2012 to 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify @@ -30,11 +30,13 @@ #include #include -#include "../zipwrap.hpp" +#include "zipwrap.hpp" extern "C" { #include "../twcommon.h" #include "gui.h" } + +#include "../minuitwrp/truetype.hpp" #include "../minuitwrp/minui.h" #include "rapidxml.hpp" @@ -155,12 +157,12 @@ void FontResource::LoadFont(xml_node<>* node, ZipWrap* pZip) std::string tmpname = "/tmp/" + file; if (ExtractResource(pZip, "fonts", file, "", tmpname) == 0) { - mFont = gr_ttf_loadFont(tmpname.c_str(), font_size, dpi); + mFont = twrpTruetype::gr_ttf_loadFont(tmpname.c_str(), font_size, dpi); } else { file = std::string(TWRES "fonts/") + file; - mFont = gr_ttf_loadFont(file.c_str(), font_size, dpi); + mFont = twrpTruetype::gr_ttf_loadFont(file.c_str(), font_size, dpi); } } else @@ -170,11 +172,13 @@ void FontResource::LoadFont(xml_node<>* node, ZipWrap* pZip) } void FontResource::DeleteFont() { - if (mFont) - gr_ttf_freeFont(mFont); + if (mFont) { + twrpTruetype::gr_ttf_freeFont(mFont); + } mFont = NULL; - if (origFont) - gr_ttf_freeFont(origFont); + if (origFont) { + twrpTruetype::gr_ttf_freeFont(origFont); + } origFont = NULL; } @@ -182,7 +186,7 @@ void FontResource::Override(xml_node<>* node, ZipWrap* pZip) { if (!origFont) { origFont = mFont; } else if (mFont) { - gr_ttf_freeFont(mFont); + twrpTruetype::gr_ttf_freeFont(mFont); mFont = NULL; } LoadFont(node, pZip); diff --git a/gui/resources.hpp b/gui/resources.hpp old mode 100644 new mode 100755 index 69bebc70e0..e709e330ea --- a/gui/resources.hpp +++ b/gui/resources.hpp @@ -1,5 +1,5 @@ /* - Copyright 2017 TeamWin + Copyright 2012 to 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify @@ -25,7 +25,8 @@ #include #include #include "rapidxml.hpp" -#include "../zipwrap.hpp" +#include "zipwrap.hpp" +#include "../minuitwrp/truetype.hpp" extern "C" { #include "../minuitwrp/minui.h" @@ -58,7 +59,7 @@ class FontResource : public Resource public: void* GetResource() { return mFont; } - int GetHeight() { return gr_ttf_getMaxFontHeight(mFont); } + int GetHeight() { return twrpTruetype::gr_ttf_getMaxFontHeight(mFont); } void Override(xml_node<>* node, ZipWrap* pZip); protected: diff --git a/gui/scrolllist.cpp b/gui/scrolllist.cpp index bf5a9b06d0..0a4eed85b2 100755 --- a/gui/scrolllist.cpp +++ b/gui/scrolllist.cpp @@ -1,5 +1,5 @@ /* - Copyright 2013 bigbiff/Dees_Troy TeamWin + Copyright 2012 to 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ extern "C" { #include "../twcommon.h" } #include "../minuitwrp/minui.h" +#include "../minuitwrp/truetype.hpp" #include "rapidxml.hpp" #include "objects.hpp" @@ -633,7 +634,7 @@ bool GUIScrollList::AddLines(std::vector* origText, std::vectorat(i); for (;;) { - size_t line_char_width = gr_ttf_maxExW(curr_line.c_str(), mFont->GetResource(), mRenderW); + size_t line_char_width = twrpTruetype::gr_ttf_maxExW(curr_line.c_str(), mFont->GetResource(), mRenderW); if (line_char_width < curr_line.size()) { //string left = curr_line.substr(0, line_char_width); size_t wrap_pos = curr_line.find_last_of(" ,./:-_;", line_char_width - 1); diff --git a/gui/slidervalue.cpp b/gui/slidervalue.cpp old mode 100644 new mode 100755 index 3aaffcc05e..5ed9c4fa78 --- a/gui/slidervalue.cpp +++ b/gui/slidervalue.cpp @@ -1,5 +1,5 @@ /* - Copyright 2017 TeamWin + Copyright 2012 to 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify @@ -39,6 +39,7 @@ extern "C" { #include "../twcommon.h" } #include "../minuitwrp/minui.h" +#include "../minuitwrp/truetype.hpp" #include "rapidxml.hpp" #include "objects.hpp" @@ -270,7 +271,7 @@ int GUISliderValue::measureText(const std::string& str) void* fontResource = NULL; if (mFont) fontResource = mFont->GetResource(); - return gr_ttf_measureEx(str.c_str(), fontResource); + return twrpTruetype::gr_ttf_measureEx(str.c_str(), fontResource); } int GUISliderValue::Render(void) diff --git a/gui/terminal.cpp b/gui/terminal.cpp index a4fed91047..d7037e1d72 100755 --- a/gui/terminal.cpp +++ b/gui/terminal.cpp @@ -34,6 +34,8 @@ extern "C" { #include "../twcommon.h" } #include "../minuitwrp/minui.h" +#include "../minuitwrp/truetype.hpp" + #include "gui.hpp" #include "rapidxml.hpp" @@ -46,7 +48,6 @@ extern "C" { #endif extern int g_pty_fd; // in gui.cpp where the select is - /* Pseudoterminal handler. */ @@ -114,7 +115,7 @@ class Pseudoterminal // (Mandatory for programs like the shell to make them manage correctly their outputs) ioctl(0, TIOCSCTTY, 1); - execl("/sbin/sh", "sh", NULL); + execl("/system/bin/sh", "sh", NULL); _exit(127); } @@ -142,7 +143,7 @@ class Pseudoterminal return -1; } int rc = ::write(fdMaster, buffer, size); - debug_printf("pty write: %d bytes -> %d\n", size, rc); + debug_printf("pty write: %zu bytes -> %d\n", size, rc); if (rc < 0) { LOGERR("pty write failed: %d\n", errno); // assume child has died @@ -386,6 +387,14 @@ class TerminalEngine output(buffer[i]); } + bool status() { + return pty.started(); + } + + void stop() { + pty.stop(); + } + void clear() { cursorX = cursorY = 0; @@ -883,10 +892,10 @@ void GUITerminal::RenderItem(size_t itemindex, int yPos, bool selected __unused) // render cursor int cursorX = engine->getCursorX(); std::string leftOfCursor = line.substr(0, cursorX); - int x = gr_ttf_measureEx(leftOfCursor.c_str(), mFont->GetResource()); + int x = twrpTruetype::gr_ttf_measureEx(leftOfCursor.c_str(), mFont->GetResource()); // note that this single character can be a UTF-8 sequence std::string atCursor = (size_t)cursorX < line.length() ? line.substr(cursorX, 1) : " "; - int w = gr_ttf_measureEx(atCursor.c_str(), mFont->GetResource()); + int w = twrpTruetype::gr_ttf_measureEx(atCursor.c_str(), mFont->GetResource()); gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha); gr_fill(mRenderX + x, yPos, w, actualItemHeight); gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha); @@ -899,13 +908,24 @@ void GUITerminal::NotifySelect(size_t item_selected __unused) // do nothing - terminal ignores selections } +bool GUITerminal::status() +{ + return engine->status(); +} + +void GUITerminal::stop() +{ + engine->stop(); + engine->clear(); +} + void GUITerminal::InitAndResize() { // make sure the shell is started engine->initPty(); // send window resize if (mFont && mFont->GetResource()) { - int charWidth = gr_ttf_measureEx("N", mFont->GetResource()); + int charWidth = twrpTruetype::gr_ttf_measureEx("N", mFont->GetResource()); engine->setSize(mRenderW / charWidth, GetDisplayItemCount(), mRenderW, mRenderH); } } diff --git a/gui/text.cpp b/gui/text.cpp old mode 100644 new mode 100755 index 123b2499d5..b72dd047c9 --- a/gui/text.cpp +++ b/gui/text.cpp @@ -1,5 +1,5 @@ /* - Copyright 2012 to 2016 bigbiff/Dees_Troy TeamWin + Copyright 2012 to 2020 TeamWin This file is part of TWRP/TeamWin Recovery Project. TWRP is free software: you can redistribute it and/or modify @@ -39,6 +39,7 @@ extern "C" { #include "../twcommon.h" } #include "../minuitwrp/minui.h" +#include "../minuitwrp/truetype.hpp" #include "rapidxml.hpp" #include "objects.hpp" @@ -159,7 +160,7 @@ int GUIText::GetCurrentBounds(int& w, int& h) h = mFontHeight; mLastValue = gui_parse_text(mText); - w = gr_ttf_measureEx(mLastValue.c_str(), fontResource); + w = twrpTruetype::gr_ttf_measureEx(mLastValue.c_str(), fontResource); return 0; } diff --git a/gui/theme/common/landscape.xml b/gui/theme/common/landscape.xml index 8244c461ed..3ac0d285e7 100755 --- a/gui/theme/common/landscape.xml +++ b/gui/theme/common/landscape.xml @@ -175,6 +175,15 @@ + + + + + +