decrypt: AOSP 10 requires the use of fscrypt
fscrypt aosp doc: https://source.android.com/security/encryption/file-based kernel fscrypt doc: https://www.kernel.org/doc/html/v4.18/filesystems/fscrypt.html This commit implements the ability for TWRP to use fscrypt to decrypt files on the fscrypt implementation. It has been implemented mostly in a new successor library to e4crypt called libtwrpfscrypt. Most of the code was ported from AOSP vold. Notable updates include: - updated policy storage by libtar - lookup of fbe policies by libtwrpfscrypt - threaded keystore operations Big thanks to Dees_Troy for the initial trailblazing of encryption in TWRP. Change-Id: I69cd2eba3693a9914e00213d4943229635d0cdae
This commit is contained in:
+35
-8
@@ -84,6 +84,11 @@ LOCAL_SRC_FILES := \
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
LOCAL_STATIC_LIBRARIES += libavb
|
||||
LOCAL_SHARED_LIBRARIES += libfs_mgr libinit
|
||||
ifeq ($(TW_INCLUDE_CRYPTO),true)
|
||||
LOCAL_CFLAGS += -DUSE_FSCRYPT -Wno-macro-redefined
|
||||
LOCAL_C_INCLUDES += bootable/recovery/crypto/fscrypt \
|
||||
bootable/recovery/crypto
|
||||
endif
|
||||
LOCAL_C_INCLUDES += \
|
||||
system/core/fs_mgr/libfs_avb/include/ \
|
||||
system/core/fs_mgr/include_fstab/ \
|
||||
@@ -91,7 +96,8 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
system/core/fs_mgr/libdm/include/ \
|
||||
system/core/fs_mgr/liblp/include/ \
|
||||
system/gsid/include/ \
|
||||
system/core/init/
|
||||
system/core/init/ \
|
||||
system/extras/ext4_utils/include
|
||||
endif
|
||||
|
||||
ifneq ($(TARGET_RECOVERY_REBOOT_SRC),)
|
||||
@@ -103,7 +109,7 @@ LOCAL_MODULE := recovery
|
||||
RECOVERY_API_VERSION := 3
|
||||
RECOVERY_FSTAB_VERSION := 2
|
||||
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
|
||||
LOCAL_CFLAGS += -Wno-unused-parameter
|
||||
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
|
||||
LOCAL_CLANG := true
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
@@ -348,7 +354,11 @@ ifeq ($(TW_INCLUDE_CRYPTO), true)
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0)
|
||||
TW_INCLUDE_CRYPTO_FBE := true
|
||||
LOCAL_CFLAGS += -DTW_INCLUDE_FBE
|
||||
LOCAL_SHARED_LIBRARIES += libe4crypt
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += libe4crypt
|
||||
endif
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
|
||||
LOCAL_CFLAGS += -DTW_INCLUDE_FBE_METADATA_DECRYPT
|
||||
endif
|
||||
@@ -439,7 +449,7 @@ endif
|
||||
|
||||
TWRP_REQUIRED_MODULES += \
|
||||
relink \
|
||||
relink_init \
|
||||
twrp_ramdisk \
|
||||
dump_image \
|
||||
erase_image \
|
||||
flash_image \
|
||||
@@ -456,7 +466,20 @@ TWRP_REQUIRED_MODULES += \
|
||||
init.recovery.hlthchrg.rc \
|
||||
init.recovery.service.rc \
|
||||
init.recovery.ldconfig.rc \
|
||||
awk
|
||||
awk \
|
||||
|
||||
ifneq ($(TW_INCLUDE_CRYPTO),)
|
||||
TWRP_REQUIRED_MODULES += \
|
||||
plat_service_contexts \
|
||||
plat_hwservice_contexts \
|
||||
vendor_hwservice_contexts \
|
||||
vndservice_contexts \
|
||||
hwservicemanager \
|
||||
servicemanager \
|
||||
vndservicemanager \
|
||||
vold_prepare_subdirs \
|
||||
fscryptpolicyget
|
||||
endif
|
||||
|
||||
ifneq ($(TARGET_ARCH), arm64)
|
||||
ifneq ($(TARGET_ARCH), x86_64)
|
||||
@@ -695,7 +718,7 @@ LOCAL_MODULE := librecovery
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libminui \
|
||||
libotautil \
|
||||
libvintf_recovery \
|
||||
libvintf \
|
||||
libcrypto_utils \
|
||||
libcrypto \
|
||||
libbase \
|
||||
@@ -720,7 +743,7 @@ else
|
||||
install/set_metadata.cpp verifier28/verifier.cpp install/zipwrap.cpp install/ZipUtil.cpp
|
||||
endif
|
||||
LOCAL_SHARED_LIBRARIES += libbase libbootloader_message libcrypto libext4_utils \
|
||||
libfs_mgr libfusesideload libhidl-gen-utils libhidlbase libhidltransport \
|
||||
libfs_mgr libfusesideload libhidl-gen-utils libhidlbase \
|
||||
liblog libselinux libtinyxml2 libutils libz libziparchive libcutils
|
||||
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
|
||||
@@ -875,7 +898,11 @@ ifeq ($(TW_INCLUDE_CRYPTO), true)
|
||||
include $(commands_TWRP_local_path)/crypto/fde/Android.mk
|
||||
include $(commands_TWRP_local_path)/crypto/scrypt/Android.mk
|
||||
ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
|
||||
include $(commands_TWRP_local_path)/crypto/ext4crypt/Android.mk
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
include $(commands_TWRP_local_path)/crypto/fscrypt/Android.mk
|
||||
else
|
||||
include $(commands_TWRP_local_path)/crypto/ext4crypt/Android.mk
|
||||
endif
|
||||
endif
|
||||
ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),)
|
||||
ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false)
|
||||
|
||||
@@ -25,7 +25,6 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
|
||||
LOCAL_CFLAGS += -DHAVE_GATEKEEPER1
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += android.hardware.confirmationui@1.0
|
||||
# LOCAL_CFLAGS += -DUSE_
|
||||
endif
|
||||
LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
|
||||
|
||||
Regular → Executable
+14
-2
@@ -17,7 +17,13 @@ endif
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
|
||||
#8.0 or higher
|
||||
LOCAL_C_INCLUDES += external/boringssl/src/include
|
||||
LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite libe4crypt android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += libe4crypt
|
||||
endif
|
||||
LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite \
|
||||
android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
|
||||
#9.0 rules
|
||||
LOCAL_CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
|
||||
@@ -72,7 +78,13 @@ endif
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
|
||||
#8.0 or higher
|
||||
LOCAL_C_INCLUDES += external/boringssl/src/include
|
||||
LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite libe4crypt android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += libe4crypt
|
||||
endif
|
||||
LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite \
|
||||
android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
|
||||
#9.0 rules
|
||||
LOCAL_CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
|
||||
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
ifeq ($(TW_INCLUDE_CRYPTO), true)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := libtwrpfscrypt
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_CFLAGS := -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment -Wno-missing-field-initializers \
|
||||
-DHAVE_LIBKEYUTILS -std=gnu++2a -Wno-macro-redefined -Wno-unused-function
|
||||
LOCAL_SRC_FILES := Decrypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp \
|
||||
FsCrypt.cpp KeyUtil.cpp Keymaster.cpp KeyStorage.cpp MetadataCrypt.cpp KeyBuffer.cpp \
|
||||
Process.cpp EncryptInplace.cpp Weaver1.cpp fscrypt_policy.cpp
|
||||
LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils \
|
||||
libkeymaster_messages libhardware libprotobuf-cpp-lite libfscrypt android.hardware.confirmationui@1.0 \
|
||||
android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0 \
|
||||
libfs_mgr android.hardware.keymaster@4.0 libkeymaster4support libf2fs_sparseblock libkeystore_parcelables \
|
||||
libkeystore_aidl android.hardware.weaver@1.0 libkeyutils liblog libhwbinder libchrome
|
||||
LOCAL_STATIC_LIBRARIES := libscrypt_static
|
||||
LOCAL_C_INCLUDES := system/extras/ext4_utils \
|
||||
system/extras/ext4_utils/include/ext4_utils \
|
||||
external/scrypt/lib/crypto \
|
||||
system/security/keystore/include \
|
||||
hardware/libhardware/include/hardware \
|
||||
system/security/softkeymaster/include/keymaster \
|
||||
system/keymaster/include \
|
||||
system/extras/libfscrypt/include \
|
||||
system/core/fs_mgr/libfs_avb/include/ \
|
||||
system/core/fs_mgr/include_fstab/ \
|
||||
system/core/fs_mgr/include/ \
|
||||
system/core/fs_mgr/libdm/include/ \
|
||||
system/core/fs_mgr/liblp/include/ \
|
||||
system/gsid/include/ \
|
||||
system/core/init/ \
|
||||
system/vold/model \
|
||||
system/vold/ \
|
||||
system/extras/f2fs_utils/
|
||||
|
||||
ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
|
||||
LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
|
||||
LOCAL_C_INCLUDES += external/boringssl/src/include
|
||||
endif
|
||||
|
||||
LOCAL_REQUIRED_MODULES := keystore_auth keystore
|
||||
LOCAL_CLANG := true
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := twrpfbe
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := main.cpp
|
||||
LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := fscryptpolicyget
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := fscryptpolicyget.cpp
|
||||
LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
|
||||
LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := keystore_auth
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := keystore_auth.cpp
|
||||
LOCAL_SHARED_LIBRARIES := libc libkeystore_binder libutils libbinder liblog
|
||||
LOCAL_CFLAGS += -DUSE_SECURITY_NAMESPACE
|
||||
LOCAL_SHARED_LIBRARIES += libkeystore_aidl
|
||||
LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
@@ -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 <binder/Status.h>
|
||||
#include <string>
|
||||
|
||||
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
|
||||
Executable
+1169
File diff suppressed because it is too large
Load Diff
Executable
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 <stdbool.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <cutils/multiuser.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
__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
|
||||
@@ -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 <ext4_utils/ext4.h>
|
||||
#include <ext4_utils/ext4_utils.h>
|
||||
#include <f2fs_sparseblock.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _ENCRYPT_INPLACE_H
|
||||
#define _ENCRYPT_INPLACE_H
|
||||
|
||||
#include <sys/types.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);
|
||||
|
||||
#endif
|
||||
Executable
+862
@@ -0,0 +1,862 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "FsCrypt.h"
|
||||
|
||||
#include "KeyStorage.h"
|
||||
#include "KeyUtil.h"
|
||||
#include "Utils.h"
|
||||
// #include "VoldUtil.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <selinux/android.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
// #include "android/os/IVold.h"
|
||||
|
||||
#include "cryptfs.h"
|
||||
|
||||
#define EMULATED_USES_SELINUX 0
|
||||
#define MANAGE_MISC_DIRS 0
|
||||
|
||||
#include <cutils/fs.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include <fscrypt/fscrypt.h>
|
||||
#include <fs_mgr.h>
|
||||
#include <keyutils.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
|
||||
using android::base::StringPrintf;
|
||||
using android::fs_mgr::GetEntryForMountPoint;
|
||||
using android::vold::kEmptyAuthentication;
|
||||
using android::vold::KeyBuffer;
|
||||
// using android::vold::writeStringToFile;
|
||||
|
||||
// Store main DE raw ref / policy
|
||||
std::string de_raw_ref;
|
||||
std::map<userid_t, std::string> s_de_key_raw_refs;
|
||||
std::map<userid_t, std::string> s_ce_key_raw_refs;
|
||||
|
||||
namespace {
|
||||
|
||||
struct PolicyKeyRef {
|
||||
std::string contents_mode;
|
||||
std::string filenames_mode;
|
||||
std::string key_raw_ref;
|
||||
};
|
||||
|
||||
const std::string device_key_dir = std::string() + DATA_MNT_POINT + fscrypt_unencrypted_folder;
|
||||
const std::string device_key_path = device_key_dir + "/key";
|
||||
const std::string device_key_temp = device_key_dir + "/temp";
|
||||
|
||||
const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys";
|
||||
const std::string user_key_temp = user_key_dir + "/temp";
|
||||
const std::string prepare_subdirs_path = "/sbin/vold_prepare_subdirs";
|
||||
|
||||
const std::string systemwide_volume_key_dir =
|
||||
std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys";
|
||||
const int STORAGE_FLAG_DE = 1;
|
||||
const int STORAGE_FLAG_CE = 2;
|
||||
|
||||
bool s_systemwide_keys_initialized = false;
|
||||
|
||||
android::fs_mgr::Fstab fstab_default;
|
||||
|
||||
// Some users are ephemeral, don't try to wipe their keys from disk
|
||||
std::set<userid_t> s_ephemeral_users;
|
||||
|
||||
// TODO abolish this map, per b/26948053
|
||||
std::map<userid_t, KeyBuffer> 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<std::string> get_ce_key_paths(const std::string& directory_path) {
|
||||
auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
|
||||
if (!dirp) {
|
||||
PLOG(ERROR) << "Unable to open ce key directory: " + directory_path;
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
std::vector<std::string> 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<std::string>();
|
||||
}
|
||||
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<std::string>& 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<std::string>& paths) {
|
||||
for (auto const other_path : paths) {
|
||||
if (other_path != to_fix) {
|
||||
android::vold::destroyKey(other_path);
|
||||
}
|
||||
}
|
||||
auto const current_path = get_ce_key_current_path(directory_path);
|
||||
if (to_fix != current_path) {
|
||||
LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path;
|
||||
if (rename(to_fix.c_str(), current_path.c_str()) != 0) {
|
||||
PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path;
|
||||
return;
|
||||
}
|
||||
}
|
||||
android::vold::FsyncDirectory(directory_path);
|
||||
}
|
||||
|
||||
static bool read_and_fixate_user_ce_key(userid_t user_id,
|
||||
const android::vold::KeyAuthentication& auth,
|
||||
KeyBuffer* ce_key) {
|
||||
auto const directory_path = get_ce_key_directory_path(user_id);
|
||||
auto const paths = get_ce_key_paths(directory_path);
|
||||
for (auto const ce_key_path : paths) {
|
||||
LOG(DEBUG) << "Trying user CE key " << ce_key_path;
|
||||
if (android::vold::retrieveKey(ce_key_path, auth, ce_key)) {
|
||||
LOG(DEBUG) << "Successfully retrieved key";
|
||||
fixate_user_ce_key(directory_path, ce_key_path, paths);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LOG(ERROR) << "Failed to find working ce key for user " << user_id;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_and_install_user_ce_key(userid_t user_id,
|
||||
const android::vold::KeyAuthentication& auth) {
|
||||
if (s_ce_key_raw_refs.count(user_id) != 0) return true;
|
||||
KeyBuffer ce_key;
|
||||
if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
|
||||
std::string ce_raw_ref;
|
||||
if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false;
|
||||
s_ce_keys[user_id] = std::move(ce_key);
|
||||
s_ce_key_raw_refs[user_id] = ce_raw_ref;
|
||||
LOG(DEBUG) << "Installed ce key for user " << user_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) {
|
||||
LOG(DEBUG) << "Preparing: " << dir;
|
||||
if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
|
||||
PLOG(ERROR) << "Failed to prepare " << dir;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool destroy_dir(const std::string& dir) {
|
||||
LOG(DEBUG) << "Destroying: " << dir;
|
||||
if (rmdir(dir.c_str()) != 0 && errno != ENOENT) {
|
||||
PLOG(ERROR) << "Failed to destroy " << dir;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// NB this assumes that there is only one thread listening for crypt commands, because
|
||||
// it creates keys in a fixed location.
|
||||
static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
|
||||
KeyBuffer de_key, ce_key;
|
||||
if (!android::vold::randomKey(&de_key)) return false;
|
||||
if (!android::vold::randomKey(&ce_key)) return false;
|
||||
if (create_ephemeral) {
|
||||
// If the key should be created as ephemeral, don't store it.
|
||||
s_ephemeral_users.insert(user_id);
|
||||
} else {
|
||||
auto const directory_path = get_ce_key_directory_path(user_id);
|
||||
if (!prepare_dir(directory_path, 0700, AID_ROOT, AID_ROOT)) return false;
|
||||
auto const paths = get_ce_key_paths(directory_path);
|
||||
std::string ce_key_path;
|
||||
if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
|
||||
if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, kEmptyAuthentication,
|
||||
ce_key))
|
||||
return false;
|
||||
fixate_user_ce_key(directory_path, ce_key_path, paths);
|
||||
// Write DE key second; once this is written, all is good.
|
||||
if (!android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp,
|
||||
kEmptyAuthentication, de_key))
|
||||
return false;
|
||||
}
|
||||
if (!android::vold::installKey(de_key, &de_raw_ref)) return false;
|
||||
s_de_key_raw_refs[user_id] = de_raw_ref;
|
||||
std::string ce_raw_ref;
|
||||
if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false;
|
||||
s_ce_keys[user_id] = ce_key;
|
||||
s_ce_key_raw_refs[user_id] = ce_raw_ref;
|
||||
LOG(DEBUG) << "Created keys for user " << user_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
|
||||
std::string* raw_ref) {
|
||||
auto refi = key_map.find(user_id);
|
||||
if (refi == key_map.end()) {
|
||||
LOG(DEBUG) << "Cannot find key for " << user_id;
|
||||
return false;
|
||||
}
|
||||
*raw_ref = refi->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) {
|
||||
if (!ReadDefaultFstab(&fstab_default)) {
|
||||
PLOG(ERROR) << "Failed to open default fstab";
|
||||
return;
|
||||
}
|
||||
auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
|
||||
if (entry == nullptr) {
|
||||
LOG(ERROR) << "get_data_file_encryption_modes::failed\n";
|
||||
return;
|
||||
}
|
||||
key_ref->contents_mode = entry->file_contents_mode;
|
||||
key_ref->filenames_mode = entry->file_names_mode;
|
||||
}
|
||||
|
||||
static bool ensure_policy(const PolicyKeyRef& key_ref, const std::string& path) {
|
||||
return fscrypt_policy_ensure(path.c_str(), key_ref.key_raw_ref.data(),
|
||||
key_ref.key_raw_ref.size(), key_ref.contents_mode.c_str(),
|
||||
key_ref.filenames_mode.c_str()) == 0;
|
||||
}
|
||||
|
||||
static bool is_numeric(const char* name) {
|
||||
for (const char* p = name; *p != '\0'; p++) {
|
||||
if (!isdigit(*p)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool load_all_de_keys() {
|
||||
auto de_dir = user_key_dir + "/de";
|
||||
auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir);
|
||||
if (!dirp) {
|
||||
PLOG(ERROR) << "Unable to read de key directory";
|
||||
return false;
|
||||
}
|
||||
for (;;) {
|
||||
errno = 0;
|
||||
auto entry = readdir(dirp.get());
|
||||
if (!entry) {
|
||||
if (errno) {
|
||||
PLOG(ERROR) << "Unable to read de key directory";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
|
||||
LOG(DEBUG) << "Skipping non-de-key " << entry->d_name;
|
||||
continue;
|
||||
}
|
||||
userid_t user_id = std::stoi(entry->d_name);
|
||||
if (s_de_key_raw_refs.count(user_id) == 0) {
|
||||
auto key_path = de_dir + "/" + entry->d_name;
|
||||
KeyBuffer key;
|
||||
if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
|
||||
std::string raw_ref;
|
||||
if (!android::vold::installKey(key, &raw_ref)) return false;
|
||||
s_de_key_raw_refs[user_id] = raw_ref;
|
||||
LOG(DEBUG) << "Installed de key for user " << user_id;
|
||||
}
|
||||
}
|
||||
// fscrypt:TODO: go through all DE directories, ensure that all user dirs have the
|
||||
// correct policy set on them, and that no rogue ones exist.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fscrypt_initialize_systemwide_keys() {
|
||||
LOG(INFO) << "fscrypt_initialize_systemwide_keys";
|
||||
|
||||
if (s_systemwide_keys_initialized) {
|
||||
LOG(INFO) << "Already initialized";
|
||||
return true;
|
||||
}
|
||||
|
||||
PolicyKeyRef device_ref;
|
||||
if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path,
|
||||
device_key_temp, &device_ref.key_raw_ref))
|
||||
return false;
|
||||
get_data_file_encryption_modes(&device_ref);
|
||||
|
||||
std::string modestring = device_ref.contents_mode + ":" + device_ref.filenames_mode;
|
||||
std::string mode_filename = std::string("/data") + fscrypt_key_mode;
|
||||
if (!android::vold::writeStringToFile(modestring, mode_filename)) return false;
|
||||
|
||||
std::string ref_filename = std::string("/data") + fscrypt_key_ref;
|
||||
if (!android::vold::writeStringToFile(device_ref.key_raw_ref, ref_filename)) return false;
|
||||
LOG(INFO) << "Wrote system DE key reference to:" << ref_filename;
|
||||
|
||||
KeyBuffer per_boot_key;
|
||||
if (!android::vold::randomKey(&per_boot_key)) return false;
|
||||
std::string per_boot_raw_ref;
|
||||
if (!android::vold::installKey(per_boot_key, &per_boot_raw_ref)) return false;
|
||||
std::string per_boot_ref_filename = std::string("/data") + fscrypt_key_per_boot_ref;
|
||||
if (!android::vold::writeStringToFile(per_boot_raw_ref, per_boot_ref_filename)) return false;
|
||||
LOG(INFO) << "Wrote per boot key reference to:" << per_boot_ref_filename;
|
||||
|
||||
if (!android::vold::FsyncDirectory(device_key_dir)) return false;
|
||||
s_systemwide_keys_initialized = true;
|
||||
de_raw_ref = device_ref.key_raw_ref;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fscrypt_init_user0() {
|
||||
if (!ReadDefaultFstab(&fstab_default)) {
|
||||
PLOG(ERROR) << "Failed to open default fstab";
|
||||
return -1;
|
||||
}
|
||||
if (fscrypt_is_native()) {
|
||||
if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
|
||||
if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
|
||||
if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
|
||||
if (!android::vold::pathExists(get_de_key_path(0))) {
|
||||
if (!create_and_install_user_keys(0, false)) return false;
|
||||
}
|
||||
// TODO: switch to loading only DE_0 here once framework makes
|
||||
// explicit calls to install DE keys for secondary users
|
||||
if (!load_all_de_keys()) return false;
|
||||
}
|
||||
// We can only safely prepare DE storage here, since CE keys are probably
|
||||
// entangled with user credentials. The framework will always prepare CE
|
||||
// storage once CE keys are installed.
|
||||
if (!fscrypt_prepare_user_storage("", 0, 0, STORAGE_FLAG_DE)) {
|
||||
LOG(ERROR) << "Failed to prepare user 0 storage";
|
||||
return false;
|
||||
}
|
||||
// If this is a non-FBE device that recently left an emulated mode,
|
||||
// restore user data directories to known-good state.
|
||||
if (!fscrypt_is_native() && !fscrypt_is_emulated()) {
|
||||
fscrypt_unlock_user_key(0, 0, "!", "!");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) {
|
||||
LOG(DEBUG) << "fscrypt_vold_create_user_key for " << user_id << " serial " << serial;
|
||||
if (!fscrypt_is_native()) {
|
||||
return true;
|
||||
}
|
||||
// FIXME test for existence of key that is not loaded yet
|
||||
if (s_ce_key_raw_refs.count(user_id) != 0) {
|
||||
LOG(ERROR) << "Already exists, can't fscrypt_vold_create_user_key for " << user_id
|
||||
<< " serial " << serial;
|
||||
// FIXME should we fail the command?
|
||||
return true;
|
||||
}
|
||||
if (!create_and_install_user_keys(user_id, ephemeral)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void drop_caches() {
|
||||
// Clean any dirty pages (otherwise they won't be dropped).
|
||||
sync();
|
||||
// Drop inode and page caches.
|
||||
if (!android::vold::writeStringToFile("3", "/proc/sys/vm/drop_caches")) {
|
||||
PLOG(ERROR) << "Failed to drop caches during key eviction";
|
||||
}
|
||||
}
|
||||
|
||||
static bool evict_ce_key(userid_t user_id) {
|
||||
s_ce_keys.erase(user_id);
|
||||
bool success = true;
|
||||
std::string raw_ref;
|
||||
// If we haven't loaded the CE key, no need to evict it.
|
||||
if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) {
|
||||
success &= android::vold::evictKey(raw_ref);
|
||||
drop_caches();
|
||||
}
|
||||
s_ce_key_raw_refs.erase(user_id);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool fscrypt_destroy_user_key(userid_t user_id) {
|
||||
LOG(DEBUG) << "fscrypt_destroy_user_key(" << user_id << ")";
|
||||
if (!fscrypt_is_native()) {
|
||||
return true;
|
||||
}
|
||||
bool success = true;
|
||||
std::string raw_ref;
|
||||
success &= evict_ce_key(user_id);
|
||||
success &=
|
||||
lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref) && android::vold::evictKey(raw_ref);
|
||||
s_de_key_raw_refs.erase(user_id);
|
||||
auto it = s_ephemeral_users.find(user_id);
|
||||
if (it != s_ephemeral_users.end()) {
|
||||
s_ephemeral_users.erase(it);
|
||||
} else {
|
||||
for (auto const path : get_ce_key_paths(get_ce_key_directory_path(user_id))) {
|
||||
success &= android::vold::destroyKey(path);
|
||||
}
|
||||
auto de_key_path = get_de_key_path(user_id);
|
||||
if (android::vold::pathExists(de_key_path)) {
|
||||
success &= android::vold::destroyKey(de_key_path);
|
||||
} else {
|
||||
LOG(INFO) << "Not present so not erasing: " << de_key_path;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool emulated_lock(const std::string& path) {
|
||||
if (chmod(path.c_str(), 0000) != 0) {
|
||||
PLOG(ERROR) << "Failed to chmod " << path;
|
||||
return false;
|
||||
}
|
||||
#if EMULATED_USES_SELINUX
|
||||
if (setfilecon(path.c_str(), "u:object_r:storage_stub_file:s0") != 0) {
|
||||
PLOG(WARNING) << "Failed to setfilecon " << path;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool emulated_unlock(const std::string& path, mode_t mode) {
|
||||
if (chmod(path.c_str(), mode) != 0) {
|
||||
PLOG(ERROR) << "Failed to chmod " << path;
|
||||
// FIXME temporary workaround for b/26713622
|
||||
if (fscrypt_is_emulated()) return false;
|
||||
}
|
||||
#if EMULATED_USES_SELINUX
|
||||
if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_FORCE) != 0) {
|
||||
PLOG(WARNING) << "Failed to restorecon " << path;
|
||||
// FIXME temporary workaround for b/26713622
|
||||
if (fscrypt_is_emulated()) return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_hex(const std::string& hex, std::string* result) {
|
||||
if (hex == "!") {
|
||||
*result = "";
|
||||
return true;
|
||||
}
|
||||
if (android::vold::HexToStr(hex, *result) != 0) {
|
||||
LOG(ERROR) << "Invalid FBE hex string"; // Don't log the string for security reasons
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string volkey_path(const std::string& misc_path, const std::string& volume_uuid) {
|
||||
return misc_path + "/vold/volume_keys/" + volume_uuid + "/default";
|
||||
}
|
||||
|
||||
static std::string volume_secdiscardable_path(const std::string& volume_uuid) {
|
||||
return systemwide_volume_key_dir + "/" + volume_uuid + "/secdiscardable";
|
||||
}
|
||||
|
||||
static bool read_or_create_volkey(const std::string& misc_path, const std::string& volume_uuid,
|
||||
PolicyKeyRef* key_ref) {
|
||||
auto secdiscardable_path = volume_secdiscardable_path(volume_uuid);
|
||||
std::string secdiscardable_hash;
|
||||
if (android::vold::pathExists(secdiscardable_path)) {
|
||||
if (!android::vold::readSecdiscardable(secdiscardable_path, &secdiscardable_hash))
|
||||
return false;
|
||||
} else {
|
||||
if (fs_mkdirs(secdiscardable_path.c_str(), 0700) != 0) {
|
||||
PLOG(ERROR) << "Creating directories for: " << secdiscardable_path;
|
||||
return false;
|
||||
}
|
||||
if (!android::vold::createSecdiscardable(secdiscardable_path, &secdiscardable_hash))
|
||||
return false;
|
||||
}
|
||||
auto key_path = volkey_path(misc_path, volume_uuid);
|
||||
if (fs_mkdirs(key_path.c_str(), 0700) != 0) {
|
||||
PLOG(ERROR) << "Creating directories for: " << key_path;
|
||||
return false;
|
||||
}
|
||||
android::vold::KeyAuthentication auth("", secdiscardable_hash);
|
||||
if (!android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp",
|
||||
&key_ref->key_raw_ref))
|
||||
return false;
|
||||
key_ref->contents_mode =
|
||||
android::base::GetProperty("ro.crypto.volume.contents_mode", "aes-256-xts");
|
||||
key_ref->filenames_mode =
|
||||
android::base::GetProperty("ro.crypto.volume.filenames_mode", "aes-256-heh");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool destroy_volkey(const std::string& misc_path, const std::string& volume_uuid) {
|
||||
auto path = volkey_path(misc_path, volume_uuid);
|
||||
if (!android::vold::pathExists(path)) return true;
|
||||
return android::vold::destroyKey(path);
|
||||
}
|
||||
|
||||
bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token_hex,
|
||||
const std::string& secret_hex) {
|
||||
LOG(DEBUG) << "fscrypt_add_user_key_auth " << user_id << " serial=" << serial
|
||||
<< " token_present=" << (token_hex != "!");
|
||||
if (!fscrypt_is_native()) return true;
|
||||
if (s_ephemeral_users.count(user_id) != 0) return true;
|
||||
std::string token, secret;
|
||||
if (!parse_hex(token_hex, &token)) return false;
|
||||
if (!parse_hex(secret_hex, &secret)) return false;
|
||||
auto auth =
|
||||
secret.empty() ? kEmptyAuthentication : android::vold::KeyAuthentication(token, secret);
|
||||
auto it = s_ce_keys.find(user_id);
|
||||
if (it == s_ce_keys.end()) {
|
||||
LOG(ERROR) << "Key not loaded into memory, can't change for user " << user_id;
|
||||
return false;
|
||||
}
|
||||
const auto& ce_key = it->second;
|
||||
auto const directory_path = get_ce_key_directory_path(user_id);
|
||||
auto const paths = get_ce_key_paths(directory_path);
|
||||
std::string ce_key_path;
|
||||
if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
|
||||
if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, auth, ce_key)) return false;
|
||||
if (!android::vold::FsyncDirectory(directory_path)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fscrypt_fixate_newest_user_key_auth(userid_t user_id) {
|
||||
LOG(DEBUG) << "fscrypt_fixate_newest_user_key_auth " << user_id;
|
||||
if (!fscrypt_is_native()) return true;
|
||||
if (s_ephemeral_users.count(user_id) != 0) return true;
|
||||
auto const directory_path = get_ce_key_directory_path(user_id);
|
||||
auto const paths = get_ce_key_paths(directory_path);
|
||||
if (paths.empty()) {
|
||||
LOG(ERROR) << "No ce keys present, cannot fixate for user " << user_id;
|
||||
return false;
|
||||
}
|
||||
fixate_user_ce_key(directory_path, paths[0], paths);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: rename to 'install' for consistency, and take flags to know which keys to install
|
||||
bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex,
|
||||
const std::string& secret_hex) {
|
||||
LOG(DEBUG) << "fscrypt_unlock_user_key " << user_id << " serial=" << serial
|
||||
<< " token_present=" << (token_hex != "!");
|
||||
if (fscrypt_is_native()) {
|
||||
if (s_ce_key_raw_refs.count(user_id) != 0) {
|
||||
LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id;
|
||||
return true;
|
||||
}
|
||||
std::string token, secret;
|
||||
if (!parse_hex(token_hex, &token)) return false;
|
||||
if (!parse_hex(secret_hex, &secret)) return false;
|
||||
android::vold::KeyAuthentication auth(token, secret);
|
||||
if (!read_and_install_user_ce_key(user_id, auth)) {
|
||||
LOG(ERROR) << "Couldn't read key for " << user_id;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// When in emulation mode, we just use chmod. However, we also
|
||||
// unlock directories when not in emulation mode, to bring devices
|
||||
// back into a known-good state.
|
||||
if (!emulated_unlock(android::vold::BuildDataSystemCePath(user_id), 0771) ||
|
||||
!emulated_unlock(android::vold::BuildDataMiscCePath(user_id), 01771) ||
|
||||
!emulated_unlock(android::vold::BuildDataMediaCePath("", user_id), 0770) ||
|
||||
!emulated_unlock(android::vold::BuildDataUserCePath("", user_id), 0771)) {
|
||||
LOG(ERROR) << "Failed to unlock user " << user_id;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: rename to 'evict' for consistency
|
||||
bool fscrypt_lock_user_key(userid_t user_id) {
|
||||
LOG(DEBUG) << "fscrypt_lock_user_key " << user_id;
|
||||
if (fscrypt_is_native()) {
|
||||
return evict_ce_key(user_id);
|
||||
} else if (fscrypt_is_emulated()) {
|
||||
// When in emulation mode, we just use chmod
|
||||
if (!emulated_lock(android::vold::BuildDataSystemCePath(user_id)) ||
|
||||
!emulated_lock(android::vold::BuildDataMiscCePath(user_id)) ||
|
||||
!emulated_lock(android::vold::BuildDataMediaCePath("", user_id)) ||
|
||||
!emulated_lock(android::vold::BuildDataUserCePath("", user_id))) {
|
||||
LOG(ERROR) << "Failed to lock user " << user_id;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prepare_subdirs(const std::string& action, const std::string& volume_uuid,
|
||||
userid_t user_id, int flags) {
|
||||
if (0 != android::vold::ForkExecvp(
|
||||
std::vector<std::string>{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<DIR, int (*)(DIR*)>(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;
|
||||
}
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <cutils/multiuser.h>
|
||||
|
||||
bool fscrypt_initialize_systemwide_keys();
|
||||
|
||||
bool fscrypt_init_user0();
|
||||
bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
|
||||
bool fscrypt_destroy_user_key(userid_t user_id);
|
||||
bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token,
|
||||
const std::string& secret);
|
||||
bool fscrypt_fixate_newest_user_key_auth(userid_t user_id);
|
||||
|
||||
bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
|
||||
const std::string& secret);
|
||||
bool fscrypt_lock_user_key(userid_t user_id);
|
||||
|
||||
bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
|
||||
int flags);
|
||||
bool fscrypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
|
||||
|
||||
bool fscrypt_destroy_volume_keys(const std::string& volume_uuid);
|
||||
|
||||
bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
|
||||
std::string* raw_ref);
|
||||
@@ -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 <stdio.h>
|
||||
#include <string>
|
||||
#include <stdlib.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -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 <string>
|
||||
|
||||
#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 <class T>
|
||||
void endianswap(T *objp) {
|
||||
unsigned char *memp = reinterpret_cast<unsigned char*>(objp);
|
||||
std::reverse(memp, memp + sizeof(T));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "KeyBuffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs) {
|
||||
std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs));
|
||||
return std::move(lhs);
|
||||
}
|
||||
|
||||
KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs) {
|
||||
std::copy(rhs, rhs + strlen(rhs), std::back_inserter(lhs));
|
||||
return std::move(lhs);
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
@@ -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 <cstring>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
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<char> {
|
||||
public:
|
||||
void deallocate(pointer p, size_type n)
|
||||
{
|
||||
memset_s(p, 0, n);
|
||||
std::allocator<char>::deallocate(p, n);
|
||||
}
|
||||
};
|
||||
|
||||
// Char vector that zeroes memory when deallocating.
|
||||
using KeyBuffer = std::vector<char, ZeroingAllocator>;
|
||||
|
||||
// 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
|
||||
|
||||
Executable
+598
@@ -0,0 +1,598 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "KeyStorage.h"
|
||||
|
||||
#include "Keymaster.h"
|
||||
#include "ScryptParameters.h"
|
||||
#include "Utils.h"
|
||||
#include "Checkpoint.h"
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <android-base/properties.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include <hardware/hw_auth_token.h>
|
||||
#include <keymasterV4_0/authorization_set.h>
|
||||
#include <keymasterV4_0/keymaster_utils.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "crypto_scrypt.h"
|
||||
}
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
const KeyAuthentication kEmptyAuthentication{"", ""};
|
||||
|
||||
static constexpr size_t AES_KEY_BYTES = 32;
|
||||
static constexpr size_t GCM_NONCE_BYTES = 12;
|
||||
static constexpr size_t GCM_MAC_BYTES = 16;
|
||||
static constexpr size_t SALT_BYTES = 1 << 4;
|
||||
static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
|
||||
static constexpr size_t STRETCHED_BYTES = 1 << 6;
|
||||
|
||||
static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
|
||||
|
||||
static const char* kCurrentVersion = "1";
|
||||
static const char* kRmPath = "/system/bin/rm";
|
||||
static const char* kSecdiscardPath = "/system/bin/secdiscard";
|
||||
static const char* kStretch_none = "none";
|
||||
static const char* kStretch_nopassword = "nopassword";
|
||||
static const std::string kStretchPrefix_scrypt = "scrypt ";
|
||||
static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
|
||||
static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
|
||||
static const char* kFn_encrypted_key = "encrypted_key";
|
||||
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
|
||||
static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
|
||||
static const char* kFn_salt = "salt";
|
||||
static const char* kFn_secdiscardable = "secdiscardable";
|
||||
static const char* kFn_stretching = "stretching";
|
||||
static const char* kFn_version = "version";
|
||||
|
||||
static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
|
||||
if (actual != expected) {
|
||||
LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
|
||||
<< actual;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) {
|
||||
SHA512_CTX c;
|
||||
|
||||
SHA512_Init(&c);
|
||||
// Personalise the hashing by introducing a fixed prefix.
|
||||
// Hashing applications should use personalization except when there is a
|
||||
// specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
|
||||
std::string hashingPrefix = prefix;
|
||||
hashingPrefix.resize(SHA512_CBLOCK);
|
||||
SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
|
||||
SHA512_Update(&c, tohash.data(), tohash.size());
|
||||
res->assign(SHA512_DIGEST_LENGTH, '\0');
|
||||
SHA512_Final(reinterpret_cast<uint8_t*>(&(*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<const hw_auth_token_t*>(auth.token.data());
|
||||
paramBuilder.Authorization(km::TAG_USER_SECURE_ID, at->user_id);
|
||||
paramBuilder.Authorization(km::TAG_USER_AUTH_TYPE, km::HardwareAuthenticatorType::PASSWORD);
|
||||
paramBuilder.Authorization(km::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
|
||||
}
|
||||
return keymaster.generateKey(paramBuilder, key);
|
||||
}
|
||||
|
||||
static std::pair<km::AuthorizationSet, km::HardwareAuthToken> 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<const char*>(&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<const uint8_t*>(secret.data()), secret.size(),
|
||||
reinterpret_cast<const uint8_t*>(salt.data()), salt.size(), 1 << Nf,
|
||||
1 << rf, 1 << pf, reinterpret_cast<uint8_t*>(&(*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, decltype(&::EVP_CIPHER_CTX_free)>(
|
||||
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<const uint8_t*>(key.data()),
|
||||
reinterpret_cast<const uint8_t*>(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<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES),
|
||||
&outlen, reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
if (outlen != static_cast<int>(plaintext.size())) {
|
||||
LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen;
|
||||
return false;
|
||||
}
|
||||
if (1 != EVP_EncryptFinal_ex(
|
||||
ctx.get(),
|
||||
reinterpret_cast<uint8_t*>(&(*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<uint8_t*>(&(*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, decltype(&::EVP_CIPHER_CTX_free)>(
|
||||
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<const uint8_t*>(key.data()),
|
||||
reinterpret_cast<const uint8_t*>(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<uint8_t*>(&(*plaintext)[0]), &outlen,
|
||||
reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES),
|
||||
plaintext->size())) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
if (outlen != static_cast<int>(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<void*>(reinterpret_cast<const void*>(
|
||||
ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
if (1 != EVP_DecryptFinal_ex(ctx.get(),
|
||||
reinterpret_cast<uint8_t*>(&(*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<std::string>{kSecdiscardPath, "--", file}) != 0) {
|
||||
LOG(ERROR) << "secdiscard failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool recursiveDeleteKey(const std::string& dir) {
|
||||
if (ForkExecvp(std::vector<std::string>{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<std::string>{
|
||||
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
|
||||
Executable
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_VOLD_KEYSTORAGE_H
|
||||
#define ANDROID_VOLD_KEYSTORAGE_H
|
||||
|
||||
#include "KeyBuffer.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
// Represents the information needed to decrypt a disk encryption key.
|
||||
// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
|
||||
// If "token" and "secret" are nonempty, "secret" is appended to the application-specific
|
||||
// binary needed to unlock.
|
||||
// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
|
||||
class KeyAuthentication {
|
||||
public:
|
||||
KeyAuthentication(const std::string& t, const std::string& s) : token{t}, secret{s} {};
|
||||
|
||||
bool usesKeymaster() const { return !token.empty() || secret.empty(); };
|
||||
|
||||
const std::string token;
|
||||
const std::string secret;
|
||||
};
|
||||
|
||||
extern const KeyAuthentication kEmptyAuthentication;
|
||||
|
||||
// Checks if path "path" exists.
|
||||
bool pathExists(const std::string& path);
|
||||
|
||||
bool createSecdiscardable(const std::string& path, std::string* hash);
|
||||
bool readSecdiscardable(const std::string& path, std::string* hash);
|
||||
|
||||
// Create a directory at the named path, and store "key" in it,
|
||||
// in such a way that it can only be retrieved via Keymaster and
|
||||
// can be securely deleted.
|
||||
// It's safe to move/rename the directory after creation.
|
||||
bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key);
|
||||
|
||||
// Create a directory at the named path, and store "key" in it as storeKey
|
||||
// This version creates the key in "tmp_path" then atomically renames "tmp_path"
|
||||
// to "key_path" thereby ensuring that the key is either stored entirely or
|
||||
// not at all.
|
||||
bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
|
||||
const KeyAuthentication& auth, const KeyBuffer& key);
|
||||
|
||||
// Retrieve the key from the named directory.
|
||||
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
|
||||
bool keepOld = false);
|
||||
|
||||
// Securely destroy the key stored in the named directory and delete the directory.
|
||||
bool destroyKey(const std::string& dir);
|
||||
|
||||
bool runSecdiscardSingle(const std::string& file);
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
#endif
|
||||
Executable
+191
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "KeyUtil.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <keyutils.h>
|
||||
|
||||
#include "KeyStorage.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
constexpr int FS_AES_256_XTS_KEY_SIZE = 64;
|
||||
|
||||
bool randomKey(KeyBuffer* key) {
|
||||
*key = KeyBuffer(FS_AES_256_XTS_KEY_SIZE);
|
||||
if (ReadRandomBytes(key->size(), key->data()) != 0) {
|
||||
// TODO status_t plays badly with PLOG, fix it.
|
||||
LOG(ERROR) << "Random read failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get raw keyref - used to make keyname and to pass to ioctl
|
||||
static std::string generateKeyRef(const uint8_t* key, int length) {
|
||||
SHA512_CTX c;
|
||||
|
||||
SHA512_Init(&c);
|
||||
SHA512_Update(&c, key, length);
|
||||
unsigned char key_ref1[SHA512_DIGEST_LENGTH];
|
||||
SHA512_Final(key_ref1, &c);
|
||||
|
||||
SHA512_Init(&c);
|
||||
SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
|
||||
unsigned char key_ref2[SHA512_DIGEST_LENGTH];
|
||||
SHA512_Final(key_ref2, &c);
|
||||
|
||||
static_assert(FS_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH, "Hash too short for descriptor");
|
||||
return std::string((char*)key_ref2, FS_KEY_DESCRIPTOR_SIZE);
|
||||
}
|
||||
|
||||
static bool fillKey(const KeyBuffer& key, fscrypt_key* fs_key) {
|
||||
if (key.size() != FS_AES_256_XTS_KEY_SIZE) {
|
||||
LOG(ERROR) << "Wrong size key " << key.size();
|
||||
return false;
|
||||
}
|
||||
static_assert(FS_AES_256_XTS_KEY_SIZE <= sizeof(fs_key->raw), "Key too long!");
|
||||
fs_key->mode = FS_ENCRYPTION_MODE_AES_256_XTS;
|
||||
fs_key->size = key.size();
|
||||
memset(fs_key->raw, 0, sizeof(fs_key->raw));
|
||||
memcpy(fs_key->raw, key.data(), key.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
static char const* const NAME_PREFIXES[] = {"ext4", "f2fs", "fscrypt", nullptr};
|
||||
|
||||
static std::string keyname(const std::string& prefix, const std::string& raw_ref) {
|
||||
std::ostringstream o;
|
||||
o << prefix << ":";
|
||||
for (unsigned char i : raw_ref) {
|
||||
o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
|
||||
}
|
||||
return o.str();
|
||||
}
|
||||
|
||||
// Get the keyring we store all keys in
|
||||
static bool fscryptKeyring(key_serial_t* device_keyring) {
|
||||
*device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "fscrypt", 0);
|
||||
if (*device_keyring == -1) {
|
||||
PLOG(ERROR) << "Unable to find device keyring";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Install password into global keyring
|
||||
// Return raw key reference for use in policy
|
||||
bool installKey(const KeyBuffer& key, std::string* raw_ref) {
|
||||
// Place fscrypt_key into automatically zeroing buffer.
|
||||
KeyBuffer fsKeyBuffer(sizeof(fscrypt_key));
|
||||
fscrypt_key& fs_key = *reinterpret_cast<fscrypt_key*>(fsKeyBuffer.data());
|
||||
|
||||
if (!fillKey(key, &fs_key)) return false;
|
||||
*raw_ref = generateKeyRef(fs_key.raw, fs_key.size);
|
||||
key_serial_t device_keyring;
|
||||
if (!fscryptKeyring(&device_keyring)) return false;
|
||||
for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
|
||||
auto ref = keyname(*name_prefix, *raw_ref);
|
||||
key_serial_t key_id =
|
||||
add_key("logon", ref.c_str(), (void*)&fs_key, sizeof(fs_key), device_keyring);
|
||||
if (key_id == -1) {
|
||||
PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring;
|
||||
return false;
|
||||
}
|
||||
LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
|
||||
<< " in process " << getpid();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool evictKey(const std::string& raw_ref) {
|
||||
key_serial_t device_keyring;
|
||||
if (!fscryptKeyring(&device_keyring)) return false;
|
||||
bool success = true;
|
||||
for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
|
||||
auto ref = keyname(*name_prefix, raw_ref);
|
||||
auto key_serial = keyctl_search(device_keyring, "logon", ref.c_str(), 0);
|
||||
|
||||
// Unlink the key from the keyring. Prefer unlinking to revoking or
|
||||
// invalidating, since unlinking is actually no less secure currently, and
|
||||
// it avoids bugs in certain kernel versions where the keyring key is
|
||||
// referenced from places it shouldn't be.
|
||||
if (keyctl_unlink(key_serial, device_keyring) != 0) {
|
||||
PLOG(ERROR) << "Failed to unlink key with serial " << key_serial << " ref " << ref;
|
||||
success = false;
|
||||
} else {
|
||||
LOG(DEBUG) << "Unlinked key with serial " << key_serial << " ref " << ref;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
|
||||
const std::string& key_path, const std::string& tmp_path,
|
||||
std::string* key_ref) {
|
||||
KeyBuffer key;
|
||||
if (pathExists(key_path)) {
|
||||
LOG(DEBUG) << "Key exists, using: " << key_path;
|
||||
if (!retrieveKey(key_path, key_authentication, &key)) return false;
|
||||
} else {
|
||||
if (!create_if_absent) {
|
||||
LOG(ERROR) << "No key found in " << key_path;
|
||||
return false;
|
||||
}
|
||||
LOG(INFO) << "Creating new key in " << key_path;
|
||||
if (!randomKey(&key)) return false;
|
||||
if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false;
|
||||
}
|
||||
|
||||
if (!installKey(key, key_ref)) {
|
||||
LOG(ERROR) << "Failed to install key in " << key_path;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
|
||||
KeyBuffer* key, bool keepOld) {
|
||||
LOG(ERROR) << "retreiveKey1";
|
||||
if (pathExists(key_path)) {
|
||||
LOG(ERROR) << "Key exists, using: " << key_path;
|
||||
if (!retrieveKey(key_path, kEmptyAuthentication, key, keepOld)) return false;
|
||||
} else {
|
||||
if (!create_if_absent) {
|
||||
LOG(ERROR) << "No key found in " << key_path;
|
||||
return false;
|
||||
}
|
||||
LOG(ERROR) << "Creating new key in " << key_path;
|
||||
if (!randomKey(key)) return false;
|
||||
LOG(ERROR) << "retrieveKey1";
|
||||
if (!storeKeyAtomically(key_path, tmp_path, kEmptyAuthentication, *key)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_VOLD_KEYUTIL_H
|
||||
#define ANDROID_VOLD_KEYUTIL_H
|
||||
|
||||
#include "KeyBuffer.h"
|
||||
#include "KeyStorage.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
bool randomKey(KeyBuffer* key);
|
||||
bool installKey(const KeyBuffer& key, std::string* raw_ref);
|
||||
bool evictKey(const std::string& raw_ref);
|
||||
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
|
||||
const std::string& key_path, const std::string& tmp_path,
|
||||
std::string* key_ref);
|
||||
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
|
||||
KeyBuffer* key, bool keepOld = true);
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
#endif
|
||||
Executable
+346
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Keymaster.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <keymasterV4_0/authorization_set.h>
|
||||
#include <keymasterV4_0/keymaster_utils.h>
|
||||
|
||||
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<void(const char*, size_t)> consumer) {
|
||||
uint32_t inputConsumed = 0;
|
||||
|
||||
km::ErrorCode km_error;
|
||||
auto hidlCB = [&](km::ErrorCode ret, uint32_t inputConsumedDelta,
|
||||
const hidl_vec<km::KeyParameter>& /*ignored*/,
|
||||
const hidl_vec<uint8_t>& _output) {
|
||||
km_error = ret;
|
||||
if (km_error != km::ErrorCode::OK) return;
|
||||
inputConsumed += inputConsumedDelta;
|
||||
consumer(reinterpret_cast<const char*>(&_output[0]), _output.size());
|
||||
};
|
||||
|
||||
while (inputConsumed != inputLen) {
|
||||
size_t toRead = static_cast<size_t>(inputLen - inputConsumed);
|
||||
auto inputBlob = km::support::blob2hidlVec(
|
||||
reinterpret_cast<const uint8_t*>(&input[inputConsumed]), toRead);
|
||||
auto error = mDevice->update(mOpHandle, hidl_vec<km::KeyParameter>(), 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<km::KeyParameter>& /*ignored*/,
|
||||
const hidl_vec<uint8_t>& _output) {
|
||||
km_error = ret;
|
||||
if (km_error != km::ErrorCode::OK) return;
|
||||
if (output) output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
|
||||
};
|
||||
auto error = mDevice->finish(mOpHandle, hidl_vec<km::KeyParameter>(), hidl_vec<uint8_t>(),
|
||||
hidl_vec<uint8_t>(), 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<uint8_t>& keyBlob,
|
||||
const km::KeyCharacteristics& /*ignored*/) {
|
||||
km_error = ret;
|
||||
if (km_error != km::ErrorCode::OK) return;
|
||||
if (key) key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
|
||||
};
|
||||
|
||||
auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
|
||||
if (!error.isOk()) {
|
||||
LOG(ERROR) << "generate_key failed: " << error.description();
|
||||
return false;
|
||||
}
|
||||
if (km_error != km::ErrorCode::OK) {
|
||||
LOG(ERROR) << "generate_key failed, code " << int32_t(km_error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Keymaster::deleteKey(const std::string& key) {
|
||||
auto keyBlob = km::support::blob2hidlVec(key);
|
||||
auto error = mDevice->deleteKey(keyBlob);
|
||||
if (!error.isOk()) {
|
||||
LOG(ERROR) << "delete_key failed: " << error.description();
|
||||
return false;
|
||||
}
|
||||
if (error != km::ErrorCode::OK) {
|
||||
LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
|
||||
std::string* newKey) {
|
||||
auto oldKeyBlob = km::support::blob2hidlVec(oldKey);
|
||||
km::ErrorCode km_error;
|
||||
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
|
||||
km_error = ret;
|
||||
if (km_error != km::ErrorCode::OK) return;
|
||||
if (newKey)
|
||||
newKey->assign(reinterpret_cast<const char*>(&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<km::KeyParameter>& _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<const char*>(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<const char*>(key_blob), key_blob_size);
|
||||
std::string input(reinterpret_cast<const char*>(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<uint8_t*>(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;
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_VOLD_KEYMASTER_H
|
||||
#define ANDROID_VOLD_KEYMASTER_H
|
||||
|
||||
#include "KeyBuffer.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <keymasterV4_0/Keymaster.h>
|
||||
#include <keymasterV4_0/authorization_set.h>
|
||||
|
||||
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 <class TI, class TO>
|
||||
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<void(const char*, size_t)> consumer);
|
||||
|
||||
KmDevice* mDevice;
|
||||
uint64_t mOpHandle;
|
||||
km::ErrorCode mError;
|
||||
DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
|
||||
friend class Keymaster;
|
||||
};
|
||||
|
||||
// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
|
||||
// part of one.
|
||||
class Keymaster {
|
||||
public:
|
||||
Keymaster();
|
||||
// false if we failed to open the keymaster device.
|
||||
explicit operator bool() { return mDevice.get() != nullptr; }
|
||||
// Generate a key in the keymaster from the given params.
|
||||
bool generateKey(const km::AuthorizationSet& inParams, std::string* key);
|
||||
// If the keymaster supports it, permanently delete a key.
|
||||
bool deleteKey(const std::string& key);
|
||||
// Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
|
||||
bool upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
|
||||
std::string* newKey);
|
||||
// Begin a new cryptographic operation, collecting output parameters if pointer is non-null
|
||||
KeymasterOperation begin(km::KeyPurpose purpose, const std::string& key,
|
||||
const km::AuthorizationSet& inParams,
|
||||
const km::HardwareAuthToken& authToken,
|
||||
km::AuthorizationSet* outParams);
|
||||
bool isSecure();
|
||||
|
||||
private:
|
||||
std::unique_ptr<KmDevice> 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
|
||||
Executable
+304
@@ -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 <algorithm>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <linux/dm-ioctl.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <cutils/fs.h>
|
||||
#include <fs_mgr.h>
|
||||
|
||||
#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<char*>(mount_point),
|
||||
const_cast<char*>(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<uint64_t>(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;
|
||||
}
|
||||
@@ -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 <string>
|
||||
|
||||
bool fscrypt_mount_metadata_encrypted(const std::string& block_device,
|
||||
const std::string& mount_point, bool needs_encrypt);
|
||||
|
||||
#endif
|
||||
@@ -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 <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fts.h>
|
||||
#include <poll.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
|
||||
#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<FILE, decltype(&fclose)>{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<pid_t> pids;
|
||||
|
||||
auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(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<DIR, int (*)(DIR*)>(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
|
||||
@@ -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
|
||||
@@ -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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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<char *>(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;
|
||||
}
|
||||
@@ -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 <stdbool.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#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
|
||||
Executable
+989
@@ -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 <android-base/chrono_utils.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <cutils/fs.h>
|
||||
#include <logwrap/logwrap.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <mntent.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#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<std::mutex> 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<std::mutex> 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<std::string> 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<std::string> 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<const char*> ConvertToArgv(const std::vector<std::string>& args) {
|
||||
std::vector<const char*> 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<std::string>* output,
|
||||
android::base::unique_fd ufd) {
|
||||
std::unique_ptr<FILE, int (*)(FILE*)> 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<std::string>& args, std::vector<std::string>* 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<char**>(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<std::string>& 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<char**>(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<char> 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_t>(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<std::string>& 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<FILE, int (*)(FILE*)> 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<std::string> 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<DIR, decltype(&closedir)> 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<DIR, decltype(&closedir)> 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
|
||||
Executable
+153
@@ -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 <android-base/macros.h>
|
||||
#include <cutils/multiuser.h>
|
||||
#include <selinux/selinux.h>
|
||||
#include <utils/Errors.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<std::string>& args, std::vector<std::string>* output = nullptr,
|
||||
security_context_t context = nullptr);
|
||||
|
||||
pid_t ForkExecvpAsync(const std::vector<std::string>& 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
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VoldUtil.h"
|
||||
|
||||
android::fs_mgr::Fstab fstab_default;
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fstab/fstab.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
extern android::fs_mgr::Fstab fstab_default;
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
|
||||
@@ -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 <android-base/logging.h>
|
||||
//#include <keystore/keymaster_tags.h>
|
||||
//#include <keystore/authorization_set.h>
|
||||
//#include <keystore/keystore_hidl_support.h>
|
||||
|
||||
#include <android/hardware/weaver/1.0/IWeaver.h>
|
||||
|
||||
#include <iostream>
|
||||
#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<uint8_t>* payload) {
|
||||
bool callbackCalled = false;
|
||||
WeaverReadStatus status;
|
||||
std::vector<uint8_t> readValue;
|
||||
uint32_t timeout;
|
||||
uint32_t keySize;
|
||||
if (!GetKeySize(&keySize))
|
||||
return false;
|
||||
std::vector<uint8_t> 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
|
||||
@@ -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 <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <android/hardware/weaver/1.0/IWeaver.h>
|
||||
#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<uint8_t>* payload);
|
||||
|
||||
private:
|
||||
sp<hardware::weaver::V1_0::IWeaver> mDevice;
|
||||
hardware::weaver::V1_0::WeaverConfig config;
|
||||
bool GottenConfig;
|
||||
|
||||
bool GetConfig();
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Weaver);
|
||||
};
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_VOLD_CRYPTFS_H
|
||||
#define ANDROID_VOLD_CRYPTFS_H
|
||||
|
||||
/* This structure starts 16,384 bytes before the end of a hardware
|
||||
* partition that is encrypted, or in a separate partition. It's location
|
||||
* is specified by a property set in init.<device>.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 <linux/types.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
|
||||
/* The current cryptfs version */
|
||||
#define CURRENT_MAJOR_VERSION 1
|
||||
#define CURRENT_MINOR_VERSION 3
|
||||
|
||||
#define CRYPT_FOOTER_OFFSET 0x4000
|
||||
#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
|
||||
#define CRYPT_PERSIST_DATA_SIZE 0x1000
|
||||
|
||||
#define MAX_CRYPTO_TYPE_NAME_LEN 64
|
||||
|
||||
#define MAX_KEY_LEN 48
|
||||
#define SALT_LEN 16
|
||||
#define SCRYPT_LEN 32
|
||||
|
||||
/* definitions of flags in the structure below */
|
||||
#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
|
||||
#define CRYPT_ENCRYPTION_IN_PROGRESS \
|
||||
0x2 /* Encryption partially completed, \
|
||||
encrypted_upto valid*/
|
||||
#define CRYPT_INCONSISTENT_STATE \
|
||||
0x4 /* Set when starting encryption, clear when \
|
||||
exit cleanly, either through success or \
|
||||
correctly marked partial encryption */
|
||||
#define CRYPT_DATA_CORRUPT \
|
||||
0x8 /* Set when encryption is fine, but the \
|
||||
underlying volume is corrupt */
|
||||
#define CRYPT_FORCE_ENCRYPTION \
|
||||
0x10 /* Set when it is time to encrypt this \
|
||||
volume on boot. Everything in this \
|
||||
structure is set up correctly as \
|
||||
though device is encrypted except \
|
||||
that the master key is encrypted with the \
|
||||
default password. */
|
||||
#define CRYPT_FORCE_COMPLETE \
|
||||
0x20 /* Set when the above encryption cycle is \
|
||||
complete. On next cryptkeeper entry, match \
|
||||
the password. If it matches fix the master \
|
||||
key and remove this flag. */
|
||||
|
||||
/* Allowed values for type in the structure below */
|
||||
#define CRYPT_TYPE_PASSWORD \
|
||||
0 /* master_key is encrypted with a password \
|
||||
* Must be zero to be compatible with pre-L \
|
||||
* devices where type is always password.*/
|
||||
#define CRYPT_TYPE_DEFAULT \
|
||||
1 /* master_key is encrypted with default \
|
||||
* password */
|
||||
#define CRYPT_TYPE_PATTERN 2 /* master_key is encrypted with a pattern */
|
||||
#define CRYPT_TYPE_PIN 3 /* master_key is encrypted with a pin */
|
||||
#define CRYPT_TYPE_MAX_TYPE 3 /* type cannot be larger than this value */
|
||||
|
||||
#define CRYPT_MNT_MAGIC 0xD0B5B1C4
|
||||
#define PERSIST_DATA_MAGIC 0xE950CD44
|
||||
|
||||
/* Key Derivation Function algorithms */
|
||||
#define KDF_PBKDF2 1
|
||||
#define KDF_SCRYPT 2
|
||||
/* Algorithms 3 & 4 deprecated before shipping outside of google, so removed */
|
||||
#define KDF_SCRYPT_KEYMASTER 5
|
||||
|
||||
/* Maximum allowed keymaster blob size. */
|
||||
#define KEYMASTER_BLOB_SIZE 2048
|
||||
|
||||
/* __le32 and __le16 defined in system/extras/ext4_utils/ext4_utils.h */
|
||||
#define __le8 unsigned char
|
||||
|
||||
#if !defined(SHA256_DIGEST_LENGTH)
|
||||
#define SHA256_DIGEST_LENGTH 32
|
||||
#endif
|
||||
|
||||
struct crypt_mnt_ftr {
|
||||
__le32 magic; /* See above */
|
||||
__le16 major_version;
|
||||
__le16 minor_version;
|
||||
__le32 ftr_size; /* in bytes, not including key following */
|
||||
__le32 flags; /* See above */
|
||||
__le32 keysize; /* in bytes */
|
||||
__le32 crypt_type; /* how master_key is encrypted. Must be a
|
||||
* CRYPT_TYPE_XXX value */
|
||||
__le64 fs_size; /* Size of the encrypted fs, in 512 byte sectors */
|
||||
__le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
|
||||
mount, set to 0 on successful mount */
|
||||
unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
|
||||
needed to decrypt this
|
||||
partition, null terminated */
|
||||
__le32 spare2; /* ignored */
|
||||
unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */
|
||||
unsigned char salt[SALT_LEN]; /* The salt used for this encryption */
|
||||
__le64 persist_data_offset[2]; /* Absolute offset to both copies of crypt_persist_data
|
||||
* on device with that info, either the footer of the
|
||||
* real_blkdevice or the metadata partition. */
|
||||
|
||||
__le32 persist_data_size; /* The number of bytes allocated to each copy of the
|
||||
* persistent data table*/
|
||||
|
||||
__le8 kdf_type; /* The key derivation function used. */
|
||||
|
||||
/* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */
|
||||
__le8 N_factor; /* (1 << N) */
|
||||
__le8 r_factor; /* (1 << r) */
|
||||
__le8 p_factor; /* (1 << p) */
|
||||
__le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
|
||||
we have to stop (e.g. power low) this is the last
|
||||
encrypted 512 byte sector.*/
|
||||
__le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
|
||||
set, hash of first block, used
|
||||
to validate before continuing*/
|
||||
|
||||
/* key_master key, used to sign the derived key which is then used to generate
|
||||
* the intermediate key
|
||||
* This key should be used for no other purposes! We use this key to sign unpadded
|
||||
* data, which is acceptable but only if the key is not reused elsewhere. */
|
||||
__le8 keymaster_blob[KEYMASTER_BLOB_SIZE];
|
||||
__le32 keymaster_blob_size;
|
||||
|
||||
/* Store scrypt of salted intermediate key. When decryption fails, we can
|
||||
check if this matches, and if it does, we know that the problem is with the
|
||||
drive, and there is no point in asking the user for more passwords.
|
||||
|
||||
Note that if any part of this structure is corrupt, this will not match and
|
||||
we will continue to believe the user entered the wrong password. In that
|
||||
case the only solution is for the user to enter a password enough times to
|
||||
force a wipe.
|
||||
|
||||
Note also that there is no need to worry about migration. If this data is
|
||||
wrong, we simply won't recognise a right password, and will continue to
|
||||
prompt. On the first password change, this value will be populated and
|
||||
then we will be OK.
|
||||
*/
|
||||
unsigned char scrypted_intermediate_key[SCRYPT_LEN];
|
||||
|
||||
/* sha of this structure with this element set to zero
|
||||
Used when encrypting on reboot to validate structure before doing something
|
||||
fatal
|
||||
*/
|
||||
unsigned char sha256[SHA256_DIGEST_LENGTH];
|
||||
};
|
||||
|
||||
/* Persistant data that should be available before decryption.
|
||||
* Things like airplane mode, locale and timezone are kept
|
||||
* here and can be retrieved by the CryptKeeper UI to properly
|
||||
* configure the phone before asking for the password
|
||||
* This is only valid if the major and minor version above
|
||||
* is set to 1.1 or higher.
|
||||
*
|
||||
* This is a 4K structure. There are 2 copies, and the code alternates
|
||||
* writing one and then clearing the previous one. The reading
|
||||
* code reads the first valid copy it finds, based on the magic number.
|
||||
* The absolute offset to the first of the two copies is kept in rev 1.1
|
||||
* and higher crypt_mnt_ftr structures.
|
||||
*/
|
||||
struct crypt_persist_entry {
|
||||
char key[PROPERTY_KEY_MAX];
|
||||
char val[PROPERTY_VALUE_MAX];
|
||||
};
|
||||
|
||||
/* Should be exactly 4K in size */
|
||||
struct crypt_persist_data {
|
||||
__le32 persist_magic;
|
||||
__le32 persist_valid_entries;
|
||||
__le32 persist_spare[30];
|
||||
struct crypt_persist_entry persist_entry[0];
|
||||
};
|
||||
|
||||
#define DATA_MNT_POINT "/data"
|
||||
|
||||
/* Return values for cryptfs_crypto_complete */
|
||||
#define CRYPTO_COMPLETE_NOT_ENCRYPTED 1
|
||||
#define CRYPTO_COMPLETE_ENCRYPTED 0
|
||||
#define CRYPTO_COMPLETE_BAD_METADATA (-1)
|
||||
#define CRYPTO_COMPLETE_PARTIAL (-2)
|
||||
#define CRYPTO_COMPLETE_INCONSISTENT (-3)
|
||||
#define CRYPTO_COMPLETE_CORRUPT (-4)
|
||||
|
||||
/* Return values for cryptfs_enable_inplace*() */
|
||||
#define ENABLE_INPLACE_OK 0
|
||||
#define ENABLE_INPLACE_ERR_OTHER (-1)
|
||||
#define ENABLE_INPLACE_ERR_DEV (-2) /* crypto_blkdev issue */
|
||||
|
||||
/* Return values for cryptfs_getfield */
|
||||
#define CRYPTO_GETFIELD_OK 0
|
||||
#define CRYPTO_GETFIELD_ERROR_NO_FIELD (-1)
|
||||
#define CRYPTO_GETFIELD_ERROR_OTHER (-2)
|
||||
#define CRYPTO_GETFIELD_ERROR_BUF_TOO_SMALL (-3)
|
||||
|
||||
/* Return values for cryptfs_setfield */
|
||||
#define CRYPTO_SETFIELD_OK 0
|
||||
#define CRYPTO_SETFIELD_ERROR_OTHER (-1)
|
||||
#define CRYPTO_SETFIELD_ERROR_FIELD_TOO_LONG (-2)
|
||||
#define CRYPTO_SETFIELD_ERROR_VALUE_TOO_LONG (-3)
|
||||
|
||||
/* Return values for persist_del_key */
|
||||
#define PERSIST_DEL_KEY_OK 0
|
||||
#define PERSIST_DEL_KEY_ERROR_OTHER (-1)
|
||||
#define PERSIST_DEL_KEY_ERROR_NO_FIELD (-2)
|
||||
|
||||
int match_multi_entry(const char* key, const char* field, unsigned index);
|
||||
int wait_and_unmount(const char* mountpoint, bool kill);
|
||||
|
||||
typedef int (*kdf_func)(const char* passwd, const unsigned char* salt, unsigned char* ikey,
|
||||
void* params);
|
||||
|
||||
int cryptfs_crypto_complete(void);
|
||||
int cryptfs_check_passwd(const char* pw);
|
||||
int cryptfs_verify_passwd(const char* pw);
|
||||
int cryptfs_restart(void);
|
||||
int cryptfs_enable(int type, const char* passwd, int no_ui);
|
||||
int cryptfs_changepw(int type, const char* newpw);
|
||||
int cryptfs_enable_default(int no_ui);
|
||||
int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev, const unsigned char* key,
|
||||
char* out_crypto_blkdev);
|
||||
int cryptfs_revert_ext_volume(const char* label);
|
||||
int cryptfs_getfield(const char* fieldname, char* value, int len);
|
||||
int cryptfs_setfield(const char* fieldname, const char* value);
|
||||
int cryptfs_mount_default_encrypted(void);
|
||||
int cryptfs_get_password_type(void);
|
||||
const char* cryptfs_get_password(void);
|
||||
void cryptfs_clear_password(void);
|
||||
int cryptfs_isConvertibleToFBE(void);
|
||||
|
||||
uint32_t cryptfs_get_keysize();
|
||||
const char* cryptfs_get_crypto_name();
|
||||
|
||||
#endif /* ANDROID_VOLD_CRYPTFS_H */
|
||||
Executable
+389
@@ -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 <array>
|
||||
|
||||
#include <asm/ioctl.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <logwrap/logwrap.h>
|
||||
#include <utils/misc.h>
|
||||
|
||||
#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<const char*, 3> argv = {"ls", "-laZ", dirname};
|
||||
int status = 0;
|
||||
auto res =
|
||||
android_fork_execvp(argv.size(), const_cast<char**>(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<DIR, int (*)(DIR*)>(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;
|
||||
}
|
||||
Executable
+63
@@ -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 <sys/cdefs.h>
|
||||
#include <stdbool.h>
|
||||
#include <cutils/multiuser.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define FS_KEY_DESCRIPTOR_SIZE_HEX (2 * FS_KEY_DESCRIPTOR_SIZE + 1)
|
||||
|
||||
/* modes not supported by upstream kernel, so not in <linux/fs.h> */
|
||||
#define FS_ENCRYPTION_MODE_AES_256_HEH 126
|
||||
#define FS_ENCRYPTION_MODE_PRIVATE 127
|
||||
|
||||
/* new definition, not yet in Bionic's <linux/fs.h> */
|
||||
#ifndef FS_ENCRYPTION_MODE_ADIANTUM
|
||||
#define FS_ENCRYPTION_MODE_ADIANTUM 9
|
||||
#endif
|
||||
|
||||
/* new definition, not yet in Bionic's <linux/fs.h> */
|
||||
#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_
|
||||
Executable
+39
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
Executable
+108
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* 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 <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#ifdef USE_SECURITY_NAMESPACE
|
||||
#include <android/security/keystore/IKeystoreService.h>
|
||||
#else
|
||||
#include <keystore/IKeystoreService.h>
|
||||
#include <keystore/authorization_set.h>
|
||||
#endif
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
|
||||
#include <keystore/keystore.h>
|
||||
|
||||
#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<IServiceManager> sm = defaultServiceManager();
|
||||
sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
|
||||
#ifdef USE_SECURITY_NAMESPACE
|
||||
sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
|
||||
#else
|
||||
sp<IKeystoreService> service = interface_cast<IKeystoreService>(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<uint8_t> 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;
|
||||
}
|
||||
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _SEHANDLE_H
|
||||
#define _SEHANDLE_H
|
||||
|
||||
#include <selinux/android.h>
|
||||
|
||||
extern struct selabel_handle* sehandle;
|
||||
|
||||
#endif
|
||||
@@ -1115,13 +1115,7 @@ void DataManager::Output_Version(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!TWFunc::Path_Exists(recoveryCacheDir)) {
|
||||
LOGINFO("Recreating %s folder.\n", recoveryCacheDir.c_str());
|
||||
if (!TWFunc::Create_Dir_Recursive(recoveryCacheDir.c_str(), S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP, 0, 0)) {
|
||||
LOGERR("DataManager::Output_Version -- Unable to make %s: %s\n", recoveryCacheDir.c_str(), strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string verPath = recoveryCacheDir + ".version";
|
||||
if (TWFunc::Path_Exists(verPath)) {
|
||||
unlink(verPath.c_str());
|
||||
|
||||
@@ -43,6 +43,9 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/../otautil/include
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libziparchive
|
||||
LOCAL_STATIC_LIBRARIES += libotautil
|
||||
ifneq ($(TW_INCLUDE_CRYPTO),)
|
||||
LOCAL_C_INCLUDES += bootable/recovery/crypto/fscrypt
|
||||
endif
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 28; echo $$?),0)
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../install/include \
|
||||
system/core/libziparchive/include/ \
|
||||
|
||||
@@ -774,7 +774,6 @@ extern "C" int gui_loadResources(void)
|
||||
#ifndef TW_OEM_BUILD
|
||||
int check = 0;
|
||||
DataManager::GetValue(TW_IS_ENCRYPTED, check);
|
||||
|
||||
if (check)
|
||||
{
|
||||
if (PageManager::LoadPackage("TWRP", TWRES "ui.xml", "decrypt"))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2013 bigbiff/Dees_Troy TeamWin
|
||||
Copyright 2020 TeamWin
|
||||
This file is part of TWRP/TeamWin Recovery Project.
|
||||
|
||||
TWRP is free software: you can redistribute it and/or modify
|
||||
@@ -261,7 +261,6 @@ void GUIPartitionList::NotifySelect(size_t item_selected)
|
||||
}
|
||||
mList.at(item_selected).selected = 1;
|
||||
mUpdate = 1;
|
||||
|
||||
DataManager::SetValue(mVariable, str);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -726,5 +726,6 @@
|
||||
<string name="flash_ab_reboot">To flash additional zips, please reboot recovery to switch to the updated slot.</string>
|
||||
<string name="ozip_decrypt_decryption">Starting Ozip Decryption...</string>
|
||||
<string name="ozip_decrypt_finish">Ozip Decryption Finished!</string>
|
||||
<string name="fbe_wipe_msg">WARNING: {1} wiped. FBE device should be booted into Android and not Recovery to set initial FBE policy after wipe.</string>
|
||||
</resources>
|
||||
</language>
|
||||
|
||||
Regular → Executable
+18
-6
@@ -14,9 +14,15 @@ LOCAL_C_INCLUDES += external/libselinux/include
|
||||
LOCAL_SHARED_LIBRARIES += libselinux
|
||||
|
||||
ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
|
||||
LOCAL_SHARED_LIBRARIES += libe4crypt
|
||||
LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
|
||||
LOCAL_CFLAGS += -DUSE_FSCRYPT
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/fscrypt
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += libe4crypt
|
||||
LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
@@ -35,9 +41,15 @@ LOCAL_C_INCLUDES += external/libselinux/include
|
||||
LOCAL_STATIC_LIBRARIES += libselinux
|
||||
|
||||
ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
|
||||
LOCAL_SHARED_LIBRARIES += libe4crypt
|
||||
LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
|
||||
LOCAL_CFLAGS += -DUSE_FSCRYPT
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/fscrypt
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += libe4crypt
|
||||
LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
Regular → Executable
+48
-4
@@ -24,22 +24,28 @@
|
||||
|
||||
#include <sys/capability.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/xattr.h>
|
||||
|
||||
#ifdef STDC_HEADERS
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#ifdef HAVE_EXT4_CRYPT
|
||||
# include "ext4crypt_tar.h"
|
||||
#include "ext4crypt_tar.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_FSCRYPT
|
||||
#include "fscrypt_policy.h"
|
||||
#endif
|
||||
|
||||
#include "android_utils.h"
|
||||
|
||||
struct tar_dev
|
||||
@@ -142,6 +148,7 @@ tar_append_file(TAR *t, const char *realname, const char *savename)
|
||||
printf("malloc ext4_encryption_policy\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (e4crypt_policy_get_struct(realname, t->th_buf.eep))
|
||||
{
|
||||
char tar_policy[EXT4_KEY_DESCRIPTOR_SIZE];
|
||||
@@ -166,6 +173,43 @@ tar_append_file(TAR *t, const char *realname, const char *savename)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_FSCRYPT
|
||||
if (TH_ISDIR(t) && t->options & TAR_STORE_FSCRYPT_POL)
|
||||
{
|
||||
if (t->th_buf.fep != NULL)
|
||||
{
|
||||
free(t->th_buf.fep);
|
||||
t->th_buf.fep = NULL;
|
||||
}
|
||||
|
||||
t->th_buf.fep = (struct fscrypt_encryption_policy*)malloc(sizeof(struct fscrypt_encryption_policy));
|
||||
if (!t->th_buf.fep) {
|
||||
printf("malloc fs_encryption_policy\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fscrypt_policy_get_struct(realname, t->th_buf.fep)) {
|
||||
uint8_t tar_policy[FS_KEY_DESCRIPTOR_SIZE];
|
||||
memset(tar_policy, 0, sizeof(tar_policy));
|
||||
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
|
||||
policy_to_hex(t->th_buf.fep->master_key_descriptor, policy_hex);
|
||||
if (lookup_ref_key(t->th_buf.fep->master_key_descriptor, &tar_policy[0])) {
|
||||
printf("found fscrypt policy '%s' - '%s' - '%s'\n", realname, tar_policy, policy_hex);
|
||||
memcpy(t->th_buf.fep->master_key_descriptor, tar_policy, FS_KEY_DESCRIPTOR_SIZE);
|
||||
} else {
|
||||
printf("failed to lookup fscrypt tar policy for '%s' - '%s'\n", realname, policy_hex);
|
||||
free(t->th_buf.fep);
|
||||
t->th_buf.fep = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// no policy found, but this is not an error as not all dirs will have a policy
|
||||
free(t->th_buf.fep);
|
||||
t->th_buf.fep = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* get posix file capabilities */
|
||||
if (TH_ISREG(t) && t->options & TAR_STORE_POSIX_CAP)
|
||||
|
||||
Regular → Executable
+85
-7
@@ -18,8 +18,14 @@
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
#ifdef HAVE_EXT4_CRYPT
|
||||
# include "ext4crypt_tar.h"
|
||||
#include "ext4crypt_tar.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_FSCRYPT
|
||||
#include "fscrypt_policy.h"
|
||||
#endif
|
||||
|
||||
#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
|
||||
@@ -33,6 +39,10 @@
|
||||
#define E4CRYPT_TAG "TWRP.security.e4crypt="
|
||||
#define E4CRYPT_TAG_LEN strlen(E4CRYPT_TAG)
|
||||
|
||||
// Used to identify fscrypt_policy in extended ('x')
|
||||
#define FSCRYPT_TAG "TWRP.security.fscrypt="
|
||||
#define FSCRYPT_TAG_LEN strlen(FSCRYPT_TAG)
|
||||
|
||||
// Used to identify Posix capabilities in extended ('x')
|
||||
#define CAPABILITIES_TAG "SCHILY.xattr.security.capability="
|
||||
#define CAPABILITIES_TAG_LEN strlen(CAPABILITIES_TAG)
|
||||
@@ -132,7 +142,7 @@ th_read(TAR *t)
|
||||
char *ptr;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("==> th_read(t=0x%lx)\n", t);
|
||||
printf("==> th_read(t=0x%p)\n", (void *)t);
|
||||
#endif
|
||||
|
||||
if (t->th_buf.gnu_longname != NULL)
|
||||
@@ -145,6 +155,12 @@ th_read(TAR *t)
|
||||
if (t->th_buf.eep != NULL)
|
||||
free(t->th_buf.eep);
|
||||
#endif
|
||||
|
||||
#ifdef USE_FSCRYPT
|
||||
if (t->th_buf.fep != NULL)
|
||||
free(t->th_buf.fep);
|
||||
#endif
|
||||
|
||||
if (t->th_buf.has_cap_data)
|
||||
{
|
||||
memset(&t->th_buf.cap_data, 0, sizeof(struct vfs_cap_data));
|
||||
@@ -178,7 +194,7 @@ th_read(TAR *t)
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf(" th_read(): GNU long linkname detected "
|
||||
"(%ld bytes, %d blocks)\n", sz, blocks);
|
||||
"(%zu bytes, %zu blocks)\n", sz, blocks);
|
||||
#endif
|
||||
t->th_buf.gnu_longlink = (char *)malloc(blocks * T_BLOCKSIZE);
|
||||
if (t->th_buf.gnu_longlink == NULL)
|
||||
@@ -189,7 +205,7 @@ th_read(TAR *t)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf(" th_read(): reading long linkname "
|
||||
"(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
|
||||
"(%zu blocks left, ptr == %p)\n", blocks-j, (void *) ptr);
|
||||
#endif
|
||||
i = tar_block_read(t, ptr);
|
||||
if (i != T_BLOCKSIZE)
|
||||
@@ -228,7 +244,7 @@ th_read(TAR *t)
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf(" th_read(): GNU long filename detected "
|
||||
"(%ld bytes, %d blocks)\n", sz, blocks);
|
||||
"(%zu bytes, %zu blocks)\n", sz, blocks);
|
||||
#endif
|
||||
t->th_buf.gnu_longname = (char *)malloc(blocks * T_BLOCKSIZE);
|
||||
if (t->th_buf.gnu_longname == NULL)
|
||||
@@ -239,7 +255,7 @@ th_read(TAR *t)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf(" th_read(): reading long filename "
|
||||
"(%d blocks left, ptr == %ld)\n", blocks-j, ptr);
|
||||
"(%zu blocks left, ptr == %p)\n", blocks-j, (void *) ptr);
|
||||
#endif
|
||||
i = tar_block_read(t, ptr);
|
||||
if (i != T_BLOCKSIZE)
|
||||
@@ -266,7 +282,7 @@ th_read(TAR *t)
|
||||
}
|
||||
}
|
||||
|
||||
// Extended headers (selinux contexts, posix file capabilities, ext4 encryption policies)
|
||||
// Extended headers (selinux contexts, posix file capabilities and encryption policies)
|
||||
while(TH_ISEXTHEADER(t) || TH_ISPOLHEADER(t))
|
||||
{
|
||||
sz = th_get_size(t);
|
||||
@@ -386,6 +402,36 @@ th_read(TAR *t)
|
||||
}
|
||||
}
|
||||
#endif // HAVE_EXT4_CRYPT
|
||||
|
||||
#ifdef USE_FSCRYPT
|
||||
start = strstr(buf, FSCRYPT_TAG);
|
||||
if (start && start+FSCRYPT_TAG_LEN < buf+len) {
|
||||
t->th_buf.fep = (struct fscrypt_encryption_policy*)malloc(sizeof(struct fscrypt_encryption_policy));
|
||||
if (!t->th_buf.fep) {
|
||||
printf("malloc fscrypt_encryption_policy\n");
|
||||
return -1;
|
||||
}
|
||||
start += FSCRYPT_TAG_LEN;
|
||||
if (*start == '0') {
|
||||
start++;
|
||||
char *newline_check = start + sizeof(struct fscrypt_encryption_policy);
|
||||
if (*newline_check != '\n')
|
||||
printf("did not find newline char in expected location, continuing anyway...\n");
|
||||
memcpy(t->th_buf.fep, start, sizeof(struct fscrypt_encryption_policy));
|
||||
#ifdef DEBUG
|
||||
printf(" th_read(): FSCrypt policy v1 detected: %i %i %i %i %s\n",
|
||||
(int)t->th_buf.fep->version,
|
||||
(int)t->th_buf.fep->contents_encryption_mode,
|
||||
(int)t->th_buf.fep->filenames_encryption_mode,
|
||||
(int)t->th_buf.fep->flags,
|
||||
t->th_buf.fep->master_key_descriptor);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
printf(" invalid fscrypt header found\n");
|
||||
}
|
||||
}
|
||||
#endif // USE_FSCRYPT
|
||||
}
|
||||
|
||||
i = th_read_internal(t);
|
||||
@@ -616,6 +662,38 @@ th_write(TAR *t)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_FSCRYPT
|
||||
if((t->options & TAR_STORE_FSCRYPT_POL) && t->th_buf.fep != NULL)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("th_write(): using fscrypt_policy %s\n",
|
||||
t->th_buf.fep->master_key_descriptor);
|
||||
#endif
|
||||
/* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *version code* *content* *newline* */
|
||||
// size newline
|
||||
sz = FSCRYPT_TAG_LEN + sizeof(struct fscrypt_encryption_policy) + 1 + 3 + 1;
|
||||
|
||||
if(sz >= 100) // another ascci digit for size
|
||||
++sz;
|
||||
|
||||
if (total_sz + sz >= T_BLOCKSIZE)
|
||||
{
|
||||
if (th_write_extended(t, &buf[0], total_sz))
|
||||
return -1;
|
||||
ptr = buf;
|
||||
total_sz = sz;
|
||||
}
|
||||
else
|
||||
total_sz += sz;
|
||||
|
||||
snprintf(ptr, T_BLOCKSIZE, "%d "FSCRYPT_TAG"0", (int)sz);
|
||||
memcpy(ptr + sz - sizeof(struct fscrypt_encryption_policy) - 1, t->th_buf.fep, sizeof(struct fscrypt_encryption_policy));
|
||||
char *nlptr = ptr + sz - 1;
|
||||
*nlptr = '\n';
|
||||
ptr += sz;
|
||||
}
|
||||
#endif
|
||||
|
||||
if((t->options & TAR_STORE_POSIX_CAP) && t->th_buf.has_cap_data)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
|
||||
Regular → Executable
+31
-1
@@ -36,8 +36,13 @@
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#ifdef HAVE_EXT4_CRYPT
|
||||
# include "ext4crypt_tar.h"
|
||||
#include "ext4crypt_tar.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_FSCRYPT
|
||||
#include "fscrypt_policy.h"
|
||||
#endif
|
||||
|
||||
#include "android_utils.h"
|
||||
|
||||
const unsigned long long progress_size = (unsigned long long)(T_BLOCKSIZE);
|
||||
@@ -572,6 +577,31 @@ tar_extract_dir(TAR *t, const char *realname)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_FSCRYPT
|
||||
if(t->th_buf.fep != NULL)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("tar_extract_file(): restoring fscrypt policy %s to dir %s\n", t->th_buf.fep->master_key_descriptor, realname);
|
||||
#endif
|
||||
uint8_t binary_policy[FS_KEY_DESCRIPTOR_SIZE];
|
||||
if (!lookup_ref_tar(t->th_buf.fep->master_key_descriptor, &binary_policy[0])) {
|
||||
printf("error looking up proper fscrypt policy for '%s' - %s\n", realname, t->th_buf.fep->master_key_descriptor);
|
||||
return -1;
|
||||
}
|
||||
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
|
||||
policy_to_hex(binary_policy, policy_hex);
|
||||
printf("restoring policy %s > '%s' to '%s'\n", t->th_buf.fep->master_key_descriptor, policy_hex, realname);
|
||||
memcpy(&t->th_buf.fep->master_key_descriptor, binary_policy, FS_KEY_DESCRIPTOR_SIZE);
|
||||
if (!fscrypt_policy_set_struct(realname, t->th_buf.fep))
|
||||
{
|
||||
printf("tar_extract_file(): failed to restore fscrypt policy to dir '%s' '%s'!!!\n", realname, policy_hex);
|
||||
//return -1; // This may not be an error in some cases, so log and ignore
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("NULL FSCRYPT\n");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Regular → Executable
+12
@@ -24,6 +24,10 @@
|
||||
# include "ext4crypt_tar.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_FSCRYPT
|
||||
#include "fscrypt_policy.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@@ -70,6 +74,9 @@ struct tar_header
|
||||
char *selinux_context;
|
||||
#ifdef HAVE_EXT4_CRYPT
|
||||
struct ext4_encryption_policy *eep;
|
||||
#endif
|
||||
#ifdef USE_FSCRYPT
|
||||
struct fscrypt_encryption_policy *fep;
|
||||
#endif
|
||||
int has_cap_data;
|
||||
struct vfs_cap_data cap_data;
|
||||
@@ -120,7 +127,12 @@ TAR;
|
||||
#define TAR_IGNORE_CRC 64 /* ignore CRC in file header */
|
||||
#define TAR_STORE_SELINUX 128 /* store selinux context */
|
||||
#define TAR_USE_NUMERIC_ID 256 /* favor numeric owner over names */
|
||||
#ifdef HAVE_EXT4_CRYPT
|
||||
#define TAR_STORE_EXT4_POL 512 /* store ext4 crypto policy */
|
||||
#endif
|
||||
#ifdef USE_FSCRYPT
|
||||
#define TAR_STORE_FSCRYPT_POL 512 /* store fscrypt crypto policy */
|
||||
#endif
|
||||
#define TAR_STORE_POSIX_CAP 1024 /* store posix file capabilities */
|
||||
#define TAR_STORE_ANDROID_USER_XATTR 2048 /* store android user.* xattr */
|
||||
|
||||
|
||||
Regular → Executable
+8
-1
@@ -25,9 +25,12 @@
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXT4_CRYPT
|
||||
# include "ext4crypt_tar.h"
|
||||
#include "ext4crypt_tar.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_FSCRYPT
|
||||
#include "fscrypt_policy.h"
|
||||
#endif
|
||||
|
||||
#ifndef _POSIX_LOGIN_NAME_MAX
|
||||
# define _POSIX_LOGIN_NAME_MAX 9
|
||||
@@ -65,6 +68,10 @@ th_print(TAR *t)
|
||||
printf(" eep = \"%s\"\n",
|
||||
(t->th_buf.eep ? t->th_buf.eep->master_key_descriptor : "[NULL]"));
|
||||
#endif
|
||||
#ifdef USE_FSCRYPT
|
||||
printf(" fep = \"%s\"\n",
|
||||
(t->th_buf.fep ? t->th_buf.fep->master_key_descriptor : (uint8_t*) "[NULL]"));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
+176
-144
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2013 to 2017 TeamWin
|
||||
Copyright 2013 to 2020 TeamWin
|
||||
This file is part of TWRP/TeamWin Recovery Project.
|
||||
|
||||
TWRP is free software: you can redistribute it and/or modify
|
||||
@@ -19,17 +19,21 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#include <sys/mount.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <libgen.h>
|
||||
#include <zlib.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <sys/param.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <iostream>
|
||||
#include <libgen.h>
|
||||
#include <pwd.h>
|
||||
#include <zlib.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "cutils/properties.h"
|
||||
#include "libblkid/include/blkid.h"
|
||||
@@ -138,7 +142,6 @@ enum TW_FSTAB_FLAGS {
|
||||
TWFLAG_LENGTH,
|
||||
TWFLAG_MOUNTTODECRYPT,
|
||||
TWFLAG_REMOVABLE,
|
||||
TWFLAG_RETAINLAYOUTVERSION,
|
||||
TWFLAG_SETTINGSSTORAGE,
|
||||
TWFLAG_STORAGE,
|
||||
TWFLAG_STORAGENAME,
|
||||
@@ -183,7 +186,6 @@ const struct flag_list tw_flags[] = {
|
||||
{ "length=", TWFLAG_LENGTH },
|
||||
{ "mounttodecrypt", TWFLAG_MOUNTTODECRYPT },
|
||||
{ "removable", TWFLAG_REMOVABLE },
|
||||
{ "retainlayoutversion", TWFLAG_RETAINLAYOUTVERSION },
|
||||
{ "settingsstorage", TWFLAG_SETTINGSSTORAGE },
|
||||
{ "storage", TWFLAG_STORAGE },
|
||||
{ "storagename=", TWFLAG_STORAGENAME },
|
||||
@@ -259,7 +261,6 @@ TWPartition::TWPartition() {
|
||||
Mount_Options = "";
|
||||
Format_Block_Size = 0;
|
||||
Ignore_Blkid = false;
|
||||
Retain_Layout_Version = false;
|
||||
Crypto_Key_Location = "";
|
||||
MTP_Storage_ID = 0;
|
||||
Can_Flash_Img = false;
|
||||
@@ -665,16 +666,10 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) {
|
||||
char crypto_blkdev[255];
|
||||
property_get("ro.crypto.fs_crypto_blkdev", crypto_blkdev, "error");
|
||||
if (strcmp(crypto_blkdev, "error") != 0) {
|
||||
DataManager::SetValue(TW_IS_DECRYPTED, 1);
|
||||
Is_Encrypted = true;
|
||||
Is_Decrypted = true;
|
||||
if (Key_Directory.empty())
|
||||
Is_FBE = false;
|
||||
else
|
||||
Is_FBE = true;
|
||||
DataManager::SetValue(TW_IS_FBE, 0);
|
||||
Set_FBE_Status();
|
||||
Decrypted_Block_Device = crypto_blkdev;
|
||||
LOGINFO("Data already decrypted, new block device: '%s'\n", crypto_blkdev);
|
||||
DataManager::SetValue(TW_IS_ENCRYPTED, 0);
|
||||
} else if (!Mount(false)) {
|
||||
if (Is_Present) {
|
||||
if (Key_Directory.empty()) {
|
||||
@@ -685,7 +680,6 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) {
|
||||
Can_Be_Mounted = false;
|
||||
Current_File_System = "emmc";
|
||||
Setup_Image();
|
||||
DataManager::SetValue(TW_IS_ENCRYPTED, 1);
|
||||
DataManager::SetValue(TW_CRYPTO_PWTYPE, cryptfs_get_password_type());
|
||||
DataManager::SetValue(TW_CRYPTO_PASSWORD, "");
|
||||
DataManager::SetValue("tw_crypto_display", "");
|
||||
@@ -700,7 +694,7 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) {
|
||||
LOGERR("Primary block device '%s' for mount point '%s' is not present!\n", Primary_Block_Device.c_str(), Mount_Point.c_str());
|
||||
}
|
||||
} else {
|
||||
|
||||
Set_FBE_Status();
|
||||
if (!Decrypt_FBE_DE()) {
|
||||
char wrappedvalue[PROPERTY_VALUE_MAX];
|
||||
property_get("fbe.data.wrappedkey", wrappedvalue, "");
|
||||
@@ -715,10 +709,12 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
DataManager::SetValue(TW_IS_ENCRYPTED, 0);
|
||||
}
|
||||
if (datamedia && (!Is_Encrypted || (Is_Encrypted && Is_Decrypted))) {
|
||||
Setup_Data_Media();
|
||||
Recreate_Media_Folder();
|
||||
if (!TWFunc::Is_Mount_Wiped("/data"))
|
||||
Recreate_Media_Folder();
|
||||
}
|
||||
#else
|
||||
if (datamedia) {
|
||||
@@ -728,8 +724,22 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void TWPartition::Set_FBE_Status() {
|
||||
DataManager::SetValue(TW_IS_DECRYPTED, 1);
|
||||
Is_Encrypted = true;
|
||||
Is_Decrypted = true;
|
||||
LOGINFO("Setup_Data_Partition::Key_Directory::%s\n", Key_Directory.c_str());
|
||||
if (Key_Directory.empty()) {
|
||||
Is_FBE = false;
|
||||
DataManager::SetValue(TW_IS_FBE, 0);
|
||||
} else {
|
||||
Is_FBE = true;
|
||||
DataManager::SetValue(TW_IS_FBE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool TWPartition::Decrypt_FBE_DE() {
|
||||
if (TWFunc::Path_Exists("/data/unencrypted/key/version")) {
|
||||
if (TWFunc::Path_Exists("/data/unencrypted/key/version")) {
|
||||
DataManager::SetValue(TW_IS_FBE, 1);
|
||||
LOGINFO("File Based Encryption is present\n");
|
||||
#ifdef TW_INCLUDE_FBE
|
||||
@@ -737,6 +747,7 @@ if (TWFunc::Path_Exists("/data/unencrypted/key/version")) {
|
||||
ExcludeAll(Mount_Point + "/unencrypted");
|
||||
//ExcludeAll(Mount_Point + "/system/users/0"); // we WILL need to retain some of this if multiple users are present or we just need to delete more folders for the extra users somewhere else
|
||||
ExcludeAll(Mount_Point + "/misc/vold/user_keys");
|
||||
ExcludeAll(Mount_Point + "/misc/vold/volume_keys");
|
||||
//ExcludeAll(Mount_Point + "/system_ce");
|
||||
//ExcludeAll(Mount_Point + "/system_de");
|
||||
//ExcludeAll(Mount_Point + "/misc_ce");
|
||||
@@ -752,6 +763,8 @@ if (TWFunc::Path_Exists("/data/unencrypted/key/version")) {
|
||||
ExcludeAll(Mount_Point + "/misc/keystore");
|
||||
ExcludeAll(Mount_Point + "/drm/kek.dat");
|
||||
ExcludeAll(Mount_Point + "/system_de/0/spblob"); // contains data needed to decrypt pixel 2
|
||||
ExcludeAll(Mount_Point + "/per_boot"); // removed each boot by init
|
||||
|
||||
int retry_count = 3;
|
||||
while (!Decrypt_DE() && --retry_count)
|
||||
usleep(2000);
|
||||
@@ -761,7 +774,6 @@ if (TWFunc::Path_Exists("/data/unencrypted/key/version")) {
|
||||
Is_Decrypted = false;
|
||||
Is_FBE = true;
|
||||
DataManager::SetValue(TW_IS_FBE, 1);
|
||||
DataManager::SetValue(TW_IS_ENCRYPTED, 1);
|
||||
string filename;
|
||||
int pwd_type = Get_Password_Type(0, filename);
|
||||
if (pwd_type < 0) {
|
||||
@@ -936,9 +948,6 @@ void TWPartition::Apply_TW_Flag(const unsigned flag, const char* str, const bool
|
||||
case TWFLAG_REMOVABLE:
|
||||
Removable = val;
|
||||
break;
|
||||
case TWFLAG_RETAINLAYOUTVERSION:
|
||||
Retain_Layout_Version = val;
|
||||
break;
|
||||
case TWFLAG_SETTINGSSTORAGE:
|
||||
Is_Settings_Storage = val;
|
||||
if (Is_Settings_Storage)
|
||||
@@ -1182,7 +1191,6 @@ void TWPartition::Setup_Data_Media() {
|
||||
DataManager::SetValue("tw_has_data_media", 1);
|
||||
backup_exclusions.add_absolute_dir("/data/data/com.google.android.music/files");
|
||||
wipe_exclusions.add_absolute_dir(Mount_Point + "/misc/vold"); // adopted storage keys
|
||||
ExcludeAll(Mount_Point + "/.layout_version");
|
||||
ExcludeAll(Mount_Point + "/system/storage.xml");
|
||||
} else {
|
||||
if (Mount(true) && TWFunc::Path_Exists(Mount_Point + "/media/0")) {
|
||||
@@ -1583,7 +1591,7 @@ bool TWPartition::Mount(bool Display_Error) {
|
||||
mkdir("/system", 0755);
|
||||
mount("/system_root/system", "/system", "auto", MS_BIND, NULL);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1650,7 +1658,7 @@ bool TWPartition::ReMount_RW(bool Display_Error) {
|
||||
bool TWPartition::Wipe(string New_File_System) {
|
||||
bool wiped = false, update_crypt = false, recreate_media = true;
|
||||
int check;
|
||||
string Layout_Filename = Mount_Point + "/.layout_version";
|
||||
fscrypt_encryption_policy policy;
|
||||
|
||||
if (!Can_Be_Wiped) {
|
||||
gui_msg(Msg(msg::kError, "cannot_wipe=Partition {1} cannot be wiped.")(Display_Name));
|
||||
@@ -1660,13 +1668,13 @@ bool TWPartition::Wipe(string New_File_System) {
|
||||
if (Mount_Point == "/cache")
|
||||
Log_Offset = 0;
|
||||
|
||||
if (Retain_Layout_Version && Mount(false) && TWFunc::Path_Exists(Layout_Filename))
|
||||
TWFunc::copy_file(Layout_Filename, "/.layout_version", 0600);
|
||||
else
|
||||
unlink("/.layout_version");
|
||||
|
||||
if (Has_Data_Media && Current_File_System == New_File_System) {
|
||||
wiped = Wipe_Data_Without_Wiping_Media();
|
||||
if (Mount_Point == "/data" && TWFunc::get_cache_dir() == AB_CACHE_DIR && !TWFunc::Is_Mount_Wiped("/data")) {
|
||||
bool created = Recreate_AB_Cache_Dir(policy);
|
||||
if (created)
|
||||
gui_msg(Msg(msg::kWarning, "fbe_wipe_msg=WARNING: {1} wiped. FBE device should be booted into Android and not Recovery to set initial FBE policy after wipe.")(TWFunc::get_cache_dir()));
|
||||
}
|
||||
recreate_media = false;
|
||||
} else {
|
||||
DataManager::GetValue(TW_RM_RF_VAR, check);
|
||||
@@ -1689,19 +1697,13 @@ bool TWPartition::Wipe(string New_File_System) {
|
||||
wiped = Wipe_NTFS();
|
||||
else {
|
||||
LOGERR("Unable to wipe '%s' -- unknown file system '%s'\n", Mount_Point.c_str(), New_File_System.c_str());
|
||||
unlink("/.layout_version");
|
||||
return false;
|
||||
}
|
||||
update_crypt = wiped;
|
||||
update_crypt = false;
|
||||
}
|
||||
|
||||
if (wiped) {
|
||||
if (Mount_Point == "/cache")
|
||||
DataManager::Output_Version();
|
||||
|
||||
if (TWFunc::Path_Exists("/.layout_version") && Mount(false))
|
||||
TWFunc::copy_file("/.layout_version", Layout_Filename, 0600);
|
||||
|
||||
if (update_crypt) {
|
||||
Setup_File_System(false);
|
||||
if (Is_Encrypted && !Is_Decrypted) {
|
||||
@@ -1716,11 +1718,8 @@ bool TWPartition::Wipe(string New_File_System) {
|
||||
}
|
||||
}
|
||||
|
||||
if (Has_Data_Media && recreate_media) {
|
||||
Recreate_Media_Folder();
|
||||
}
|
||||
if (Is_Storage && Mount(false))
|
||||
PartitionManager.Add_MTP_Storage(MTP_Storage_ID);
|
||||
// if (Is_Storage && Mount(false))
|
||||
// PartitionManager.Add_MTP_Storage(MTP_Storage_ID);
|
||||
}
|
||||
|
||||
return wiped;
|
||||
@@ -2010,10 +2009,10 @@ bool TWPartition::Wipe_Encryption() {
|
||||
gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
|
||||
return false;
|
||||
}
|
||||
if (!UnMount(true))
|
||||
goto exit;
|
||||
|
||||
#ifdef TW_INCLUDE_CRYPTO
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
if (Is_Decrypted && !Decrypted_Block_Device.empty()) {
|
||||
if (delete_crypto_blk_dev((char*)("userdata")) != 0) {
|
||||
LOGERR("Error deleting crypto block device, continuing anyway.\n");
|
||||
@@ -2026,14 +2025,16 @@ bool TWPartition::Wipe_Encryption() {
|
||||
Is_Encrypted = false;
|
||||
if (Wipe(Fstab_File_System)) {
|
||||
Has_Data_Media = Save_Data_Media;
|
||||
if (Has_Data_Media && !Symlink_Mount_Point.empty()) {
|
||||
Recreate_Media_Folder();
|
||||
if (Mount(false))
|
||||
PartitionManager.Add_MTP_Storage(MTP_Storage_ID);
|
||||
}
|
||||
// if (Has_Data_Media && !Symlink_Mount_Point.empty()) {
|
||||
// if (Mount(false))
|
||||
// PartitionManager.Add_MTP_Storage(MTP_Storage_ID);
|
||||
// }
|
||||
DataManager::SetValue(TW_IS_ENCRYPTED, 0);
|
||||
#ifndef TW_OEM_BUILD
|
||||
gui_msg("format_data_msg=You may need to reboot recovery to be able to use /data again.");
|
||||
if (Is_FBE)
|
||||
gui_msg(Msg(msg::kWarning, "fbe_wipe_msg=WARNING: {1} wiped. FBE device should be booted into Android and not Recovery to set initial FBE policy after wipe.")(TWFunc::get_cache_dir()));
|
||||
else
|
||||
gui_msg("format_data_msg=You may need to reboot recovery to be able to use /data again.");
|
||||
#endif
|
||||
ret = true;
|
||||
if (!Key_Directory.empty())
|
||||
@@ -2099,6 +2100,9 @@ void TWPartition::Check_FS_Type() {
|
||||
}
|
||||
|
||||
bool TWPartition::Wipe_EXTFS(string File_System) {
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
|
||||
#if PLATFORM_SDK_VERSION < 28
|
||||
if (!TWFunc::Path_Exists("/sbin/mke2fs"))
|
||||
#else
|
||||
@@ -2115,8 +2119,6 @@ bool TWPartition::Wipe_EXTFS(string File_System) {
|
||||
gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
|
||||
return false;
|
||||
}
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
|
||||
/**
|
||||
* On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper,
|
||||
@@ -2184,14 +2186,15 @@ bool TWPartition::Wipe_EXT4() {
|
||||
int ret;
|
||||
bool NeedPreserveFooter = true;
|
||||
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
|
||||
Find_Actual_Block_Device();
|
||||
if (!Is_Present) {
|
||||
LOGINFO("Block device not present, cannot wipe %s.\n", Display_Name.c_str());
|
||||
gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
|
||||
return false;
|
||||
}
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
|
||||
/**
|
||||
* On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper,
|
||||
@@ -2239,10 +2242,10 @@ bool TWPartition::Wipe_EXT4() {
|
||||
bool TWPartition::Wipe_FAT() {
|
||||
string command;
|
||||
|
||||
if (TWFunc::Path_Exists("/sbin/mkfs.fat")) {
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
|
||||
if (TWFunc::Path_Exists("/sbin/mkfs.fat")) {
|
||||
gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mkfs.fat"));
|
||||
Find_Actual_Block_Device();
|
||||
command = "mkfs.fat " + Actual_Block_Device;
|
||||
@@ -2266,10 +2269,9 @@ bool TWPartition::Wipe_FAT() {
|
||||
bool TWPartition::Wipe_EXFAT() {
|
||||
string command;
|
||||
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
if (TWFunc::Path_Exists("/sbin/mkexfatfs")) {
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
|
||||
gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mkexfatfs"));
|
||||
Find_Actual_Block_Device();
|
||||
command = "mkexfatfs " + Actual_Block_Device;
|
||||
@@ -2335,92 +2337,73 @@ bool TWPartition::Wipe_RMRF() {
|
||||
}
|
||||
|
||||
bool TWPartition::Wipe_F2FS() {
|
||||
string command;
|
||||
std::string command;
|
||||
std::string f2fs_bin;
|
||||
|
||||
if (TWFunc::Path_Exists("/sbin/mkfs.f2fs")) {
|
||||
bool NeedPreserveFooter = true;
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
|
||||
Find_Actual_Block_Device();
|
||||
if (!Is_Present) {
|
||||
LOGINFO("Block device not present, cannot wipe %s.\n", Display_Name.c_str());
|
||||
gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
|
||||
return false;
|
||||
}
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
|
||||
/**
|
||||
* On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper,
|
||||
* so there's no need to preserve footer.
|
||||
*/
|
||||
if ((Is_Decrypted && !Decrypted_Block_Device.empty()) ||
|
||||
Crypto_Key_Location != "footer") {
|
||||
NeedPreserveFooter = false;
|
||||
}
|
||||
|
||||
gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)("mkfs.f2fs"));
|
||||
// First determine if we have the old mkfs.f2fs that uses "-r reserved_bytes"
|
||||
// or the new mkfs.f2fs that expects the number of sectors as the optional last argument
|
||||
// Note: some 7.1 trees have the old and some have the new.
|
||||
command = "mkfs.f2fs | grep \"reserved\" > /tmp/f2fsversiontest";
|
||||
TWFunc::Exec_Cmd(command, false); // no help argument so printing usage exits with an error code
|
||||
if (!TWFunc::Path_Exists("/tmp/f2fsversiontest")) {
|
||||
LOGINFO("Error determining mkfs.f2fs version\n");
|
||||
return false;
|
||||
}
|
||||
if (TWFunc::Get_File_Size("/tmp/f2fsversiontest") <= 0) {
|
||||
LOGINFO("Using newer mkfs.f2fs\n");
|
||||
unsigned long long dev_sz = TWFunc::IOCTL_Get_Block_Size(Actual_Block_Device.c_str());
|
||||
if (!dev_sz)
|
||||
return false;
|
||||
|
||||
if (NeedPreserveFooter)
|
||||
Length < 0 ? dev_sz += Length : dev_sz -= CRYPT_FOOTER_OFFSET;
|
||||
|
||||
char dev_sz_str[48];
|
||||
sprintf(dev_sz_str, "%llu", (dev_sz / 4096));
|
||||
command = "mkfs.f2fs -d1 -f -O encrypt -O quota -O verity -w 4096 " + Actual_Block_Device + " " + dev_sz_str;
|
||||
if (TWFunc::Path_Exists("/sbin/sload.f2fs")) {
|
||||
command += " && sload.f2fs -t /data " + Actual_Block_Device;
|
||||
}
|
||||
} else {
|
||||
LOGINFO("Using older mkfs.f2fs\n");
|
||||
command = "mkfs.f2fs -t 0";
|
||||
if (NeedPreserveFooter) {
|
||||
// Only use length if we're not decrypted
|
||||
char len[32];
|
||||
int mod_length = Length;
|
||||
if (Length < 0)
|
||||
mod_length *= -1;
|
||||
sprintf(len, "%i", mod_length);
|
||||
command += " -r ";
|
||||
command += len;
|
||||
}
|
||||
command += " " + Actual_Block_Device;
|
||||
}
|
||||
LOGINFO("mkfs.f2fs command: %s\n", command.c_str());
|
||||
if (TWFunc::Exec_Cmd(command) == 0) {
|
||||
if (NeedPreserveFooter)
|
||||
Wipe_Crypto_Key();
|
||||
Recreate_AndSec_Folder();
|
||||
gui_msg("done=Done.");
|
||||
return true;
|
||||
} else {
|
||||
gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (TWFunc::Path_Exists("/sbin/mkfs.f2fs"))
|
||||
f2fs_bin = "/sbin/mkfs.f2fs";
|
||||
else if (TWFunc::Path_Exists("/sbin/make_f2fs"))
|
||||
f2fs_bin = "/sbin/make_f2fs";
|
||||
else {
|
||||
LOGINFO("mkfs.f2fs binary not found, using rm -rf to wipe.\n");
|
||||
return Wipe_RMRF();
|
||||
}
|
||||
return false;
|
||||
|
||||
bool NeedPreserveFooter = true;
|
||||
|
||||
Find_Actual_Block_Device();
|
||||
if (!Is_Present) {
|
||||
LOGINFO("Block device not present, cannot wipe %s.\n", Display_Name.c_str());
|
||||
gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long long dev_sz = TWFunc::IOCTL_Get_Block_Size(Actual_Block_Device.c_str());
|
||||
if (!dev_sz)
|
||||
return false;
|
||||
|
||||
if (NeedPreserveFooter)
|
||||
Length < 0 ? dev_sz += Length : dev_sz -= CRYPT_FOOTER_OFFSET;
|
||||
|
||||
char dev_sz_str[48];
|
||||
sprintf(dev_sz_str, "%llu", (dev_sz / 4096));
|
||||
command = f2fs_bin + " -d1 -f -O encrypt -O quota -O verity -w 4096 " + Actual_Block_Device + " " + dev_sz_str;
|
||||
if (TWFunc::Path_Exists("/sbin/sload.f2fs")) {
|
||||
command += " && sload.f2fs -t /data " + Actual_Block_Device;
|
||||
}
|
||||
|
||||
/**
|
||||
* On decrypted devices, IOCTL_Get_Block_Size calculates size on device mapper,
|
||||
* so there's no need to preserve footer.
|
||||
*/
|
||||
if ((Is_Decrypted && !Decrypted_Block_Device.empty()) ||
|
||||
Crypto_Key_Location != "footer") {
|
||||
NeedPreserveFooter = false;
|
||||
}
|
||||
LOGINFO("mkfs.f2fs command: %s\n", f2fs_bin.c_str());
|
||||
if (TWFunc::Exec_Cmd(command) == 0) {
|
||||
if (NeedPreserveFooter)
|
||||
Wipe_Crypto_Key();
|
||||
Recreate_AndSec_Folder();
|
||||
gui_msg("done=Done.");
|
||||
return true;
|
||||
} else {
|
||||
gui_msg(Msg(msg::kError, "unable_to_wipe=Unable to wipe {1}.")(Display_Name));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TWPartition::Wipe_NTFS() {
|
||||
string command;
|
||||
string Ntfsmake_Binary;
|
||||
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
|
||||
if (TWFunc::Path_Exists("/sbin/mkntfs"))
|
||||
Ntfsmake_Binary = "mkntfs";
|
||||
else if (TWFunc::Path_Exists("/sbin/mkfs.ntfs"))
|
||||
@@ -2428,9 +2411,6 @@ bool TWPartition::Wipe_NTFS() {
|
||||
else
|
||||
return false;
|
||||
|
||||
if (!UnMount(true))
|
||||
return false;
|
||||
|
||||
gui_msg(Msg("formatting_using=Formatting {1} using {2}...")(Display_Name)(Ntfsmake_Binary));
|
||||
Find_Actual_Block_Device();
|
||||
command = "/sbin/" + Ntfsmake_Binary + " " + Actual_Block_Device;
|
||||
@@ -2463,6 +2443,59 @@ bool TWPartition::Wipe_Data_Without_Wiping_Media() {
|
||||
#endif // ifdef TW_OEM_BUILD
|
||||
}
|
||||
|
||||
bool TWPartition::Recreate_AB_Cache_Dir(const fscrypt_encryption_policy &policy) {
|
||||
struct passwd pd;
|
||||
struct passwd *pwdptr = &pd;
|
||||
struct passwd *tempPd;
|
||||
char pwdBuf[512];
|
||||
int uid = 0, gid = 0;
|
||||
|
||||
if ((getpwnam_r("system", pwdptr, pwdBuf, sizeof(pwdBuf), &tempPd)) != 0) {
|
||||
LOGERR("unable to get system user id\n");
|
||||
return false;
|
||||
} else {
|
||||
struct group grp;
|
||||
struct group *grpptr = &grp;
|
||||
struct group *tempGrp;
|
||||
char grpBuf[512];
|
||||
|
||||
if ((getgrnam_r("cache", grpptr, grpBuf, sizeof(grpBuf), &tempGrp)) != 0) {
|
||||
LOGERR("unable to get cache group id\n");
|
||||
return false;
|
||||
} else {
|
||||
uid = pd.pw_uid;
|
||||
gid = grp.gr_gid;
|
||||
|
||||
if (!TWFunc::Create_Dir_Recursive(AB_CACHE_DIR, S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP, uid, gid)) {
|
||||
LOGERR("Unable to recreate %s\n", AB_CACHE_DIR);
|
||||
return false;
|
||||
}
|
||||
if (setfilecon(AB_CACHE_DIR, "u:object_r:cache_file:s0") != 0) {
|
||||
LOGERR("Unable to set contexts for %s\n", AB_CACHE_DIR);
|
||||
return false;
|
||||
}
|
||||
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
|
||||
policy_to_hex(policy.master_key_descriptor, policy_hex);
|
||||
LOGINFO("setting policy for %s: %s\n", policy_hex, AB_CACHE_DIR);
|
||||
if (sizeof(policy.master_key_descriptor) > 0) {
|
||||
if (!TWFunc::Set_Encryption_Policy(AB_CACHE_DIR, policy)) {
|
||||
LOGERR("Unable to set encryption policy for %s\n", AB_CACHE_DIR);
|
||||
LOGINFO("Removing %s\n", AB_CACHE_DIR);
|
||||
int ret = TWFunc::removeDir(AB_CACHE_DIR, true);
|
||||
if (ret == -1) {
|
||||
LOGERR("Unable to remove %s\n", AB_CACHE_DIR);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOGERR("Not setting empty policy to %s\n", AB_CACHE_DIR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TWPartition::Wipe_Data_Without_Wiping_Media_Func(const string& parent __unused) {
|
||||
string dir;
|
||||
|
||||
@@ -3331,7 +3364,6 @@ int TWPartition::Decrypt_Adopted() {
|
||||
PartitionManager.Remove_Partition_By_Path("/sd-ext");
|
||||
}
|
||||
Setup_Data_Media();
|
||||
Recreate_Media_Folder();
|
||||
Wipe_Available_in_GUI = true;
|
||||
Wipe_During_Factory_Reset = true;
|
||||
Can_Be_Backed_Up = true;
|
||||
|
||||
+84
-59
@@ -43,6 +43,7 @@
|
||||
#include <android-base/chrono_utils.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <fstab/fstab.h>
|
||||
#include <fs_avb/fs_avb.h>
|
||||
@@ -84,19 +85,23 @@ extern "C" {
|
||||
}
|
||||
|
||||
#ifdef TW_INCLUDE_CRYPTO
|
||||
#include "crypto/fde/cryptfs.h"
|
||||
#include "gui/rapidxml.hpp"
|
||||
#include "gui/pages.hpp"
|
||||
#ifdef TW_INCLUDE_FBE
|
||||
#include "crypto/ext4crypt/Decrypt.h"
|
||||
#ifdef TW_INCLUDE_FBE_METADATA_DECRYPT
|
||||
#include "crypto/ext4crypt/MetadataCrypt.h"
|
||||
#endif
|
||||
#endif
|
||||
#ifdef TW_CRYPTO_USE_SYSTEM_VOLD
|
||||
#include "crypto/vold_decrypt/vold_decrypt.h"
|
||||
#include "crypto/fde/cryptfs.h"
|
||||
#include "gui/rapidxml.hpp"
|
||||
#include "gui/pages.hpp"
|
||||
#ifdef TW_INCLUDE_FBE
|
||||
#include "crypto/ext4crypt/Decrypt.h"
|
||||
#ifdef TW_INCLUDE_FBE_METADATA_DECRYPT
|
||||
#ifdef USE_FSCRYPT
|
||||
#include "crypto/fscrypt/MetadataCrypt.h"
|
||||
#else
|
||||
#include "crypto/ext4crypt/MetadataCrypt.h"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifdef TW_CRYPTO_USE_SYSTEM_VOLD
|
||||
#include "crypto/vold_decrypt/vold_decrypt.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef AB_OTA_UPDATER
|
||||
#include <hardware/hardware.h>
|
||||
@@ -307,13 +312,59 @@ int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error,
|
||||
if (settings_partition) {
|
||||
Setup_Settings_Storage_Partition(settings_partition);
|
||||
}
|
||||
#ifdef TW_INCLUDE_CRYPTO
|
||||
|
||||
Update_System_Details();
|
||||
UnMount_Main_Partitions();
|
||||
#ifdef AB_OTA_UPDATER
|
||||
DataManager::SetValue("tw_active_slot", Get_Active_Slot_Display());
|
||||
#endif
|
||||
setup_uevent();
|
||||
return true;
|
||||
}
|
||||
|
||||
int TWPartitionManager::Write_Fstab(void) {
|
||||
FILE *fp;
|
||||
std::vector<TWPartition*>::iterator iter;
|
||||
string Line;
|
||||
|
||||
fp = fopen("/etc/fstab", "w");
|
||||
if (fp == NULL) {
|
||||
LOGINFO("Can not open /etc/fstab.\n");
|
||||
return false;
|
||||
}
|
||||
for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
|
||||
if ((*iter)->Can_Be_Mounted) {
|
||||
Line = (*iter)->Actual_Block_Device + " " + (*iter)->Mount_Point + " " + (*iter)->Current_File_System + " rw 0 0\n";
|
||||
fputs(Line.c_str(), fp);
|
||||
}
|
||||
// Handle subpartition tracking
|
||||
if ((*iter)->Is_SubPartition) {
|
||||
TWPartition* ParentPartition = Find_Partition_By_Path((*iter)->SubPartition_Of);
|
||||
if (ParentPartition)
|
||||
ParentPartition->Has_SubPartition = true;
|
||||
else
|
||||
LOGERR("Unable to locate parent partition '%s' of '%s'\n", (*iter)->SubPartition_Of.c_str(), (*iter)->Mount_Point.c_str());
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TWPartitionManager::Decrypt_Data() {
|
||||
#ifdef TW_INCLUDE_CRYPTO
|
||||
TWPartition* Decrypt_Data = Find_Partition_By_Path("/data");
|
||||
if (Decrypt_Data && Decrypt_Data->Is_Encrypted && !Decrypt_Data->Is_Decrypted) {
|
||||
if (!Decrypt_Data->Key_Directory.empty() && Mount_By_Path(Decrypt_Data->Key_Directory, false)) {
|
||||
#ifdef TW_INCLUDE_FBE_METADATA_DECRYPT
|
||||
#ifdef USE_FSCRYPT
|
||||
if (fscrypt_mount_metadata_encrypted(Decrypt_Data->Actual_Block_Device, Decrypt_Data->Mount_Point, false)) {
|
||||
std::string crypto_blkdev = android::base::GetProperty("ro.crypto.fs_crypto_blkdev", "error");
|
||||
LOGINFO("Successfully decrypted metadata encrypted data partition with new block device: '%s'\n", crypto_blkdev.c_str());
|
||||
#else
|
||||
if (e4crypt_mount_metadata_encrypted(Decrypt_Data->Mount_Point, false, Decrypt_Data->Key_Directory, Decrypt_Data->Actual_Block_Device, &Decrypt_Data->Decrypted_Block_Device)) {
|
||||
LOGINFO("Successfully decrypted metadata encrypted data partition with new block device: '%s'\n", Decrypt_Data->Decrypted_Block_Device.c_str());
|
||||
LOGINFO("Successfully decrypted metadata encrypted data partition with new block device: '%s'\n",
|
||||
Decrypt_Data->Decrypted_Block_Device.c_str());
|
||||
#endif
|
||||
property_set("ro.crypto.state", "encrypted");
|
||||
Decrypt_Data->Is_Decrypted = true; // Needed to make the mount function work correctly
|
||||
int retry_count = 10;
|
||||
@@ -373,41 +424,6 @@ int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error,
|
||||
Decrypt_Adopted();
|
||||
}
|
||||
#endif
|
||||
Update_System_Details();
|
||||
UnMount_Main_Partitions();
|
||||
#ifdef AB_OTA_UPDATER
|
||||
DataManager::SetValue("tw_active_slot", Get_Active_Slot_Display());
|
||||
#endif
|
||||
setup_uevent();
|
||||
return true;
|
||||
}
|
||||
|
||||
int TWPartitionManager::Write_Fstab(void) {
|
||||
FILE *fp;
|
||||
std::vector<TWPartition*>::iterator iter;
|
||||
string Line;
|
||||
|
||||
fp = fopen("/etc/fstab", "w");
|
||||
if (fp == NULL) {
|
||||
LOGINFO("Can not open /etc/fstab.\n");
|
||||
return false;
|
||||
}
|
||||
for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
|
||||
if ((*iter)->Can_Be_Mounted) {
|
||||
Line = (*iter)->Actual_Block_Device + " " + (*iter)->Mount_Point + " " + (*iter)->Current_File_System + " rw 0 0\n";
|
||||
fputs(Line.c_str(), fp);
|
||||
}
|
||||
// Handle subpartition tracking
|
||||
if ((*iter)->Is_SubPartition) {
|
||||
TWPartition* ParentPartition = Find_Partition_By_Path((*iter)->SubPartition_Of);
|
||||
if (ParentPartition)
|
||||
ParentPartition->Has_SubPartition = true;
|
||||
else
|
||||
LOGERR("Unable to locate parent partition '%s' of '%s'\n", (*iter)->SubPartition_Of.c_str(), (*iter)->Mount_Point.c_str());
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TWPartitionManager::Setup_Settings_Storage_Partition(TWPartition* Part) {
|
||||
@@ -479,8 +495,6 @@ void TWPartitionManager::Output_Partition(TWPartition* Part) {
|
||||
printf("Is_Settings_Storage ");
|
||||
if (Part->Ignore_Blkid)
|
||||
printf("Ignore_Blkid ");
|
||||
if (Part->Retain_Layout_Version)
|
||||
printf("Retain_Layout_Version ");
|
||||
if (Part->Mount_To_Decrypt)
|
||||
printf("Mount_To_Decrypt ");
|
||||
if (Part->Can_Flash_Img)
|
||||
@@ -877,10 +891,12 @@ int TWPartitionManager::Run_Backup(bool adbbackup) {
|
||||
|
||||
LOGINFO("Calculating backup details...\n");
|
||||
DataManager::GetValue("tw_backup_list", Backup_List);
|
||||
LOGINFO("Backup_List: %s\n", Backup_List.c_str());
|
||||
if (!Backup_List.empty()) {
|
||||
end_pos = Backup_List.find(";", start_pos);
|
||||
while (end_pos != string::npos && start_pos < Backup_List.size()) {
|
||||
backup_path = Backup_List.substr(start_pos, end_pos - start_pos);
|
||||
LOGINFO("backup_path: %s\n", backup_path.c_str());
|
||||
part_settings.Part = Find_Partition_By_Path(backup_path);
|
||||
if (part_settings.Part != NULL) {
|
||||
partition_count++;
|
||||
@@ -1503,9 +1519,6 @@ int TWPartitionManager::Format_Data(void) {
|
||||
TWPartition* dat = Find_Partition_By_Path("/data");
|
||||
|
||||
if (dat != NULL) {
|
||||
if (!dat->UnMount(true))
|
||||
return false;
|
||||
|
||||
return dat->Wipe_Encryption();
|
||||
} else {
|
||||
gui_msg(Msg(msg::kError, "unable_to_locate=Unable to locate {1}.")("/data"));
|
||||
@@ -1665,11 +1678,13 @@ void TWPartitionManager::Update_System_Details(void) {
|
||||
TWPartition* FreeStorage = Find_Partition_By_Path(current_storage_path);
|
||||
if (FreeStorage != NULL) {
|
||||
// Attempt to mount storage
|
||||
if (!FreeStorage->Mount(false)) {
|
||||
gui_msg(Msg(msg::kError, "unable_to_mount_storage=Unable to mount storage"));
|
||||
DataManager::SetValue(TW_STORAGE_FREE_SIZE, 0);
|
||||
} else {
|
||||
DataManager::SetValue(TW_STORAGE_FREE_SIZE, (int)(FreeStorage->Free / 1048576LLU));
|
||||
if (!TWFunc::Is_Mount_Wiped("/data")) {
|
||||
if (!FreeStorage->Mount(false)) {
|
||||
gui_msg(Msg(msg::kWarning, "unable_to_mount_storage=Unable to mount storage"));
|
||||
DataManager::SetValue(TW_STORAGE_FREE_SIZE, 0);
|
||||
} else {
|
||||
DataManager::SetValue(TW_STORAGE_FREE_SIZE, (int)(FreeStorage->Free / 1048576LLU));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGINFO("Unable to find storage partition '%s'.\n", current_storage_path.c_str());
|
||||
@@ -1681,6 +1696,9 @@ void TWPartitionManager::Update_System_Details(void) {
|
||||
|
||||
void TWPartitionManager::Post_Decrypt(const string& Block_Device) {
|
||||
TWPartition* dat = Find_Partition_By_Path("/data");
|
||||
#ifdef USE_FSCRYPT
|
||||
dat->Set_Block_Device("/dev/block/mapper/userdata");
|
||||
#endif
|
||||
if (dat != NULL) {
|
||||
DataManager::SetValue(TW_IS_DECRYPTED, 1);
|
||||
dat->Is_Decrypted = true;
|
||||
@@ -3326,8 +3344,14 @@ std::string TWPartitionManager::Get_Super_Partition() {
|
||||
void TWPartitionManager::Setup_Super_Devices() {
|
||||
std::string superPart = Get_Super_Partition();
|
||||
android::fs_mgr::CreateLogicalPartitions(superPart);
|
||||
}
|
||||
|
||||
void TWPartitionManager::Setup_Super_Partition() {
|
||||
TWPartition* superPartition = new TWPartition();
|
||||
superPartition->Mount_Point = "super";
|
||||
std::string superPart = Get_Super_Partition();
|
||||
|
||||
superPartition->Backup_Path = "/super";
|
||||
superPartition->Mount_Point = "/super";
|
||||
superPartition->Actual_Block_Device = superPart;
|
||||
superPartition->Alternate_Block_Device = superPart;
|
||||
superPartition->Backup_Display_Name = "super";
|
||||
@@ -3336,6 +3360,7 @@ void TWPartitionManager::Setup_Super_Devices() {
|
||||
superPartition->Can_Be_Backed_Up = true;
|
||||
superPartition->Is_Present = true;
|
||||
superPartition->Is_SubPartition = false;
|
||||
superPartition->Setup_Image();
|
||||
Add_Partition(superPartition);
|
||||
PartitionManager.Output_Partition(superPartition);
|
||||
Update_System_Details();
|
||||
|
||||
+5
-2
@@ -26,6 +26,7 @@
|
||||
#include "exclude.hpp"
|
||||
#include "tw_atomic.hpp"
|
||||
#include "progresstracking.hpp"
|
||||
#include "fscrypt_policy.h"
|
||||
|
||||
#define MAX_FSTAB_LINE_LENGTH 2048
|
||||
|
||||
@@ -185,10 +186,10 @@ protected:
|
||||
private:
|
||||
bool Process_Fstab_Line(const char *fstab_line, bool Display_Error, std::map<string, Flags_Map> *twrp_flags, bool Sar_Detect); // Processes a fstab line
|
||||
void Setup_Data_Partition(bool Display_Error); // Setup data partition after fstab processed
|
||||
void Set_FBE_Status(); // Set FBE status of partition
|
||||
void Setup_Cache_Partition(bool Display_Error); // Setup cache partition after fstab processed
|
||||
bool Find_Wildcard_Block_Devices(const string& Device); // Searches for and finds wildcard block devices
|
||||
void Find_Actual_Block_Device(); // Determines the correct block device and stores it in Actual_Block_Device
|
||||
|
||||
void Apply_TW_Flag(const unsigned flag, const char* str, const bool val); // Apply custom twrp fstab flags
|
||||
void Process_TW_Flags(char *flags, bool Display_Error, int fstab_ver); // Process custom twrp fstab flags
|
||||
void Process_FS_Flags(const char *str); // Process standard fstab fs flags
|
||||
@@ -212,6 +213,7 @@ private:
|
||||
bool Wipe_NTFS(); // Uses mkntfs to wipe
|
||||
bool Wipe_Data_Without_Wiping_Media(); // Uses rm -rf to wipe but does not wipe /data/media
|
||||
bool Wipe_Data_Without_Wiping_Media_Func(const string& parent); // Uses rm -rf to wipe but does not wipe /data/media
|
||||
bool Recreate_AB_Cache_Dir(const fscrypt_encryption_policy &policy); // Recreate AB_CACHE_DIR after wipe
|
||||
void Wipe_Crypto_Key(); // Wipe crypto key from either footer or block device
|
||||
bool Backup_Tar(PartitionSettings *part_settings, pid_t *tar_fork_pid); // Backs up using tar for file systems
|
||||
bool Backup_Image(PartitionSettings *part_settings); // Backs up using raw read/write for emmc memory types
|
||||
@@ -279,7 +281,6 @@ private:
|
||||
string Mount_Options; // File system options from recovery.fstab
|
||||
unsigned long Format_Block_Size; // Block size for formatting
|
||||
bool Ignore_Blkid; // Ignore blkid results due to superblocks lying to us on certain devices / partitions
|
||||
bool Retain_Layout_Version; // Retains the .layout_version file during a wipe (needed on devices like Sony Xperia T where /data and /data/media are separate partitions)
|
||||
bool Can_Flash_Img; // Indicates if this partition can have images flashed to it via the GUI
|
||||
bool Mount_Read_Only; // Only mount this partition as read-only
|
||||
bool Is_Adopted_Storage; // Indicates that this partition is for adopted storage (android_expand)
|
||||
@@ -312,6 +313,7 @@ public:
|
||||
public:
|
||||
int Process_Fstab(string Fstab_Filename, bool Display_Error, bool Sar_Detect); // Parses the fstab and populates the partitions
|
||||
int Write_Fstab(); // Creates /etc/fstab file that's used by the command line for mount commands
|
||||
void Decrypt_Data(); // Decrypt Data if enabled
|
||||
void Output_Partition_Logging(); // Outputs partition information to the log
|
||||
void Output_Partition(TWPartition* Part); // Outputs partition details to the log
|
||||
int Mount_By_Path(string Path, bool Display_Error); // Mounts partition based on path (e.g. /system)
|
||||
@@ -390,6 +392,7 @@ public:
|
||||
std::string Get_Super_Partition(); // Get Super Partition block device path
|
||||
void Setup_Super_Devices(); // Setup logical dm devices on super partition
|
||||
bool Get_Super_Status(); // Return whether device has a super partition
|
||||
void Setup_Super_Partition(); // Setup the super partition for backup and restore
|
||||
|
||||
private:
|
||||
void Setup_Settings_Storage_Partition(TWPartition* Part); // Sets up settings storage
|
||||
|
||||
+38
-7
@@ -110,11 +110,25 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libminijail.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libunwindstack.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libasyncio.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libcgrouprc.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libbinderthreadstate.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libsquashfs_utils.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libjsoncpp.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libmdnssd.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libfec.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libinit.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libdl_android.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libprotobuf-cpp-lite.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libbinder.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/bin/toybox
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libchrome.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libevent.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/bin/keystore
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/bin/keystore_cli_v2
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/bin/hwservicemanager
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/bin/servicemanager
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/bin/vold_prepare_subdirs
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../vendor/bin/vndservicemanager
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/bin/toybox
|
||||
else
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libc.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libdl.so
|
||||
@@ -255,7 +269,15 @@ ifeq ($(TW_INCLUDE_DUMLOCK), true)
|
||||
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/htcdumlock
|
||||
endif
|
||||
ifeq ($(TW_INCLUDE_CRYPTO), true)
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcryptfsfde.so
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libcryptfsfde.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libdexfile_support.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libf2fs_sparseblock.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../vendor/lib64/libnos_transport.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../vendor/lib64/libnos_datagram.so
|
||||
else
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcryptfsfde.so
|
||||
endif
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcrypto.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libhardware.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgpt_twrp.so
|
||||
@@ -268,7 +290,11 @@ ifeq ($(TW_INCLUDE_CRYPTO), true)
|
||||
endif
|
||||
# FBE files
|
||||
ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libe4crypt.so
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
|
||||
RELINK_SOURCE_FILES += $(TARGET_ROOT_OUT)/../system/lib64/libtwrpfscrypt.so
|
||||
else
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libe4crypt.so
|
||||
endif
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgatekeeper.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster_messages.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeystore_binder.so
|
||||
@@ -514,16 +540,21 @@ include $(BUILD_PHONY_PACKAGE)
|
||||
|
||||
#relink init
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := relink_init
|
||||
LOCAL_MODULE := twrp_ramdisk
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)
|
||||
RELINK_INIT := $(TARGET_RECOVERY_ROOT_OUT)/system/bin/init
|
||||
LOCAL_POST_INSTALL_CMD += $(RELINK) $(TARGET_RECOVERY_ROOT_OUT)/ $(RELINK_INIT) && \
|
||||
mv $(TARGET_RECOVERY_ROOT_OUT)/system/bin/ueventd $(TARGET_RECOVERY_ROOT_OUT)/sbin/ && \
|
||||
cp $(TARGET_RECOVERY_ROOT_OUT)/system/bin/ueventd $(TARGET_RECOVERY_ROOT_OUT)/sbin/ && \
|
||||
ln -sf /init $(TARGET_RECOVERY_ROOT_OUT)/sbin/init && \
|
||||
ln -sf /init $(TARGET_RECOVERY_ROOT_OUT)/system/bin/init
|
||||
LOCAL_REQUIRED_MODULES := init_second_stage.recovery reboot.recovery
|
||||
ln -sf /init $(TARGET_RECOVERY_ROOT_OUT)/system/bin/init && \
|
||||
mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/system/etc/selinux/ && \
|
||||
cp $(TARGET_ROOT_OUT)/../system/etc/selinux/plat_service_contexts $(TARGET_RECOVERY_ROOT_OUT)/system/etc/selinux/plat_service_contexts && \
|
||||
cp $(TARGET_ROOT_OUT)/../system/etc/selinux/plat_hwservice_contexts $(TARGET_RECOVERY_ROOT_OUT)/system/etc/selinux/plat_hwservice_contexts && \
|
||||
cp $(TARGET_ROOT_OUT)/../vendor/etc/selinux/vndservice_contexts $(TARGET_RECOVERY_ROOT_OUT)/system/etc/selinux/vndservice_contexts && \
|
||||
cp $(TARGET_ROOT_OUT)/../vendor/etc/selinux/vendor_hwservice_contexts $(TARGET_RECOVERY_ROOT_OUT)/system/etc/selinux/vendor_hwservice_contexts
|
||||
LOCAL_REQUIRED_MODULES := init_second_stage.recovery reboot.recovery plat_service_contexts plat_hardware_contexts vndservice_contexts
|
||||
include $(BUILD_PHONY_PACKAGE)
|
||||
|
||||
#mke2fs.conf
|
||||
|
||||
@@ -18,6 +18,7 @@ process_file()
|
||||
|
||||
sed "s|/system/bin/linker64\x0|/sbin/linker64\x0\x0\x0\x0\x0\x0\x0|g" $src | sed "s|/system/bin/linker\x0|/sbin/linker\x0\x0\x0\x0\x0\x0\x0|g" | sed "s|/system/bin/sh\x0|/sbin/sh\x0\x0\x0\x0\x0\x0\x0|g" > $dst
|
||||
#rm -f $src
|
||||
[ -e $2.tmp ] && rm $2.tmp || echo
|
||||
}
|
||||
|
||||
|
||||
|
||||
+48
-1
@@ -649,7 +649,8 @@ void TWFunc::Update_Intent_File(string Intent) {
|
||||
int TWFunc::tw_reboot(RebootCommand command)
|
||||
{
|
||||
DataManager::Flush();
|
||||
Update_Log_File();
|
||||
if (!Is_Mount_Wiped("/data"))
|
||||
Update_Log_File();
|
||||
// Always force a sync before we reboot
|
||||
sync();
|
||||
|
||||
@@ -1356,4 +1357,50 @@ void TWFunc::List_Mounts() {
|
||||
}
|
||||
}
|
||||
|
||||
bool TWFunc::Get_Encryption_Policy(fscrypt_encryption_policy &policy, std::string path) {
|
||||
if (!TWFunc::Path_Exists(path)) {
|
||||
LOGERR("Unable to find %s to get policy\n", path.c_str());
|
||||
return false;
|
||||
}
|
||||
if (!fscrypt_policy_get_struct(path.c_str(), &policy)) {
|
||||
LOGERR("No policy set for path %s\n", path.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TWFunc::Set_Encryption_Policy(std::string path, const fscrypt_encryption_policy &policy) {
|
||||
if (!TWFunc::Path_Exists(path)) {
|
||||
LOGERR("unable to find %s to set policy\n", path.c_str());
|
||||
return false;
|
||||
}
|
||||
uint8_t binary_policy[FS_KEY_DESCRIPTOR_SIZE];
|
||||
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
|
||||
policy_to_hex(binary_policy, policy_hex);
|
||||
if (!fscrypt_policy_set_struct(path.c_str(), &policy)) {
|
||||
LOGERR("unable to set policy for path: %s\n", path.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TWFunc::Is_Mount_Wiped(std::string path) {
|
||||
DIR* d = opendir(path.c_str());
|
||||
size_t file_count = 0;
|
||||
if (d != NULL) {
|
||||
struct dirent* de;
|
||||
while ((de = readdir(d)) != NULL) {
|
||||
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
|
||||
continue;
|
||||
if (strncmp(de->d_name, "lost+found", 10) == 0 || strncmp(de->d_name, "media", 5) == 0 || strncmp(de->d_name, "misc", 4) == 0
|
||||
|| strncmp(de->d_name, "system", 6) == 0 || strncmp(de->d_name, "unencrypted", 11) == 0
|
||||
|| strncmp(de->d_name, "per_boot", 8) == 0)
|
||||
continue;
|
||||
file_count++;
|
||||
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
return file_count == 0;
|
||||
}
|
||||
#endif // ndef BUILD_TWRPTAR_MAIN
|
||||
|
||||
@@ -114,6 +114,9 @@ public:
|
||||
static void check_selinux_support(); // print whether selinux support is enabled to console
|
||||
static bool Is_TWRP_App_In_System(); // Check if the TWRP app is installed in the system partition
|
||||
static int Property_Override(string Prop_Name, string Prop_Value); // Override properties (including ro. properties)
|
||||
static bool Get_Encryption_Policy(fscrypt_encryption_policy &policy, std::string path); // return encryption policy for path
|
||||
static bool Set_Encryption_Policy(std::string path, const fscrypt_encryption_policy &policy); // set encryption policy for path
|
||||
static bool Is_Mount_Wiped(std::string path); // check if directory has been wiped
|
||||
static void List_Mounts();
|
||||
|
||||
private:
|
||||
|
||||
@@ -70,6 +70,31 @@ static void Print_Prop(const char *key, const char *name, void *cookie) {
|
||||
printf("%s=%s\n", key, name);
|
||||
}
|
||||
|
||||
static void Decrypt_Page(bool SkipDecryption, bool datamedia) {
|
||||
// Offer to decrypt if the device is encrypted
|
||||
if (DataManager::GetIntValue(TW_IS_ENCRYPTED) != 0) {
|
||||
if (SkipDecryption) {
|
||||
LOGINFO("Skipping decryption\n");
|
||||
} else {
|
||||
LOGINFO("Is encrypted, do decrypt page first\n");
|
||||
if (gui_startPage("decrypt", 1, 1) != 0) {
|
||||
LOGERR("Failed to start decrypt GUI page.\n");
|
||||
} else {
|
||||
// Check for and load custom theme if present
|
||||
TWFunc::check_selinux_support();
|
||||
gui_loadCustomResources();
|
||||
}
|
||||
}
|
||||
} else if (datamedia) {
|
||||
TWFunc::check_selinux_support();
|
||||
if (tw_get_default_metadata(DataManager::GetSettingsStoragePath().c_str()) != 0) {
|
||||
LOGINFO("Failed to get default contexts and file mode for storage files.\n");
|
||||
} else {
|
||||
LOGINFO("Got default contexts and file mode for storage files.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Recovery needs to install world-readable files, so clear umask
|
||||
// set by init
|
||||
@@ -199,9 +224,19 @@ int main(int argc, char **argv) {
|
||||
return -1;
|
||||
}
|
||||
PartitionManager.Output_Partition_Logging();
|
||||
#ifdef TW_INCLUDE_CRYPTO
|
||||
DataManager::SetValue(TW_IS_ENCRYPTED, 1);
|
||||
#endif
|
||||
|
||||
if (PartitionManager.Get_Super_Status())
|
||||
if (PartitionManager.Get_Super_Status()) {
|
||||
PartitionManager.Setup_Super_Devices();
|
||||
PartitionManager.Setup_Super_Partition();
|
||||
} else {
|
||||
#ifdef TW_INCLUDE_CRYPTO
|
||||
if (!PartitionManager.Get_Super_Status())
|
||||
PartitionManager.Decrypt_Data();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Load up all the resources
|
||||
gui_loadResources();
|
||||
@@ -310,28 +345,8 @@ int main(int argc, char **argv) {
|
||||
LOGINFO("Backup of TWRP ramdisk done.\n");
|
||||
#endif
|
||||
|
||||
// Offer to decrypt if the device is encrypted
|
||||
if (DataManager::GetIntValue(TW_IS_ENCRYPTED) != 0) {
|
||||
if (SkipDecryption) {
|
||||
LOGINFO("Skipping decryption\n");
|
||||
} else {
|
||||
LOGINFO("Is encrypted, do decrypt page first\n");
|
||||
if (gui_startPage("decrypt", 1, 1) != 0) {
|
||||
LOGERR("Failed to start decrypt GUI page.\n");
|
||||
} else {
|
||||
// Check for and load custom theme if present
|
||||
TWFunc::check_selinux_support();
|
||||
gui_loadCustomResources();
|
||||
}
|
||||
}
|
||||
} else if (datamedia) {
|
||||
TWFunc::check_selinux_support();
|
||||
if (tw_get_default_metadata(DataManager::GetSettingsStoragePath().c_str()) != 0) {
|
||||
LOGINFO("Failed to get default contexts and file mode for storage files.\n");
|
||||
} else {
|
||||
LOGINFO("Got default contexts and file mode for storage files.\n");
|
||||
}
|
||||
}
|
||||
if (!PartitionManager.Get_Super_Status())
|
||||
Decrypt_Page(SkipDecryption, datamedia);
|
||||
|
||||
// Fixup the RTC clock on devices which require it
|
||||
if (crash_counter == 0)
|
||||
@@ -339,7 +354,9 @@ int main(int argc, char **argv) {
|
||||
|
||||
// Read the settings file
|
||||
TWFunc::Update_Log_File();
|
||||
DataManager::ReadSettingsFile();
|
||||
|
||||
if (!PartitionManager.Get_Super_Status())
|
||||
DataManager::ReadSettingsFile();
|
||||
PageManager::LoadLanguage(DataManager::GetStrValue("tw_language"));
|
||||
GUIConsole::Translate_Now();
|
||||
|
||||
@@ -388,6 +405,11 @@ int main(int argc, char **argv) {
|
||||
LOGERR("Unable to load apex images from %s\n", APEX_DIR);
|
||||
}
|
||||
property_set("twrp.apex.loaded", "true");
|
||||
#ifdef TW_INCLUDE_CRYPTO
|
||||
PartitionManager.Decrypt_Data();
|
||||
Decrypt_Page(SkipDecryption, datamedia);
|
||||
DataManager::ReadSettingsFile();
|
||||
#endif
|
||||
} else {
|
||||
if ((DataManager::GetIntValue("tw_mount_system_ro") == 0 && sys->Check_Lifetime_Writes() == 0) || DataManager::GetIntValue("tw_mount_system_ro") == 2) {
|
||||
if (DataManager::GetIntValue("tw_never_show_system_ro_page") == 0) {
|
||||
@@ -410,6 +432,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
twrpAdbBuFifo *adb_bu_fifo = new twrpAdbBuFifo();
|
||||
adb_bu_fifo->threadAdbBuFifo();
|
||||
|
||||
@@ -424,7 +447,8 @@ int main(int argc, char **argv) {
|
||||
// Reboot
|
||||
TWFunc::Update_Intent_File(Send_Intent);
|
||||
delete adb_bu_fifo;
|
||||
TWFunc::Update_Log_File();
|
||||
if (!TWFunc::Is_Mount_Wiped("/data"))
|
||||
TWFunc::Update_Log_File();
|
||||
gui_msg(Msg("rebooting=Rebooting..."));
|
||||
string Reboot_Arg;
|
||||
DataManager::GetValue("tw_reboot_arg", Reboot_Arg);
|
||||
|
||||
Regular → Executable
+18
-2
@@ -1,6 +1,6 @@
|
||||
|
||||
/*
|
||||
Copyright 2013 to 2016 bigbiff/Dees_Troy TeamWin
|
||||
Copyright 2013 to 2020 TeamWin
|
||||
This file is part of TWRP/TeamWin Recovery Project.
|
||||
|
||||
TWRP is free software: you can redistribute it and/or modify
|
||||
@@ -47,6 +47,7 @@ extern "C" {
|
||||
#include "twrp-functions.hpp"
|
||||
#include "gui/gui.hpp"
|
||||
#include "progresstracking.hpp"
|
||||
|
||||
#ifndef BUILD_TWRPTAR_MAIN
|
||||
#include "data.hpp"
|
||||
#include "infomanager.hpp"
|
||||
@@ -54,8 +55,19 @@ extern "C" {
|
||||
#endif //ndef BUILD_TWRPTAR_MAIN
|
||||
|
||||
#ifdef TW_INCLUDE_FBE
|
||||
#ifdef USE_FSCRYPT
|
||||
#include "fscrypt_policy.h"
|
||||
#else
|
||||
#include "crypto/ext4crypt/ext4crypt_tar.h"
|
||||
#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR |TAR_STORE_EXT4_POL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef TW_INCLUDE_FBE
|
||||
#ifdef USE_FSCRYPT
|
||||
#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR | TAR_STORE_FSCRYPT_POL
|
||||
#else
|
||||
#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR | TAR_STORE_EXT4_POL
|
||||
#endif
|
||||
#else
|
||||
#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR
|
||||
#endif
|
||||
@@ -79,8 +91,12 @@ twrpTar::twrpTar(void) {
|
||||
output_fd = -1;
|
||||
backup_exclusions = NULL;
|
||||
#ifdef TW_INCLUDE_FBE
|
||||
#ifdef USE_FSCRYPT
|
||||
fscrypt_set_mode();
|
||||
#else
|
||||
e4crypt_set_mode();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
twrpTar::~twrpTar(void) {
|
||||
|
||||
Reference in New Issue
Block a user