for android-10 This will support updating the ramdisk to a different compression format and co-exist with magisk. We are also cleaning up and removing non android-10 api makefile actions. We are also moving twrp repacking to its own class. We check the new ramdisk format and if it's different we have magisk compress using the new ramdisk format. Change-Id: I770030aae7797e75817178b2f0fccd9f39dc23af
1442 lines
44 KiB
C++
Executable File
1442 lines
44 KiB
C++
Executable File
/*
|
|
Copyright 2017 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/>.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/time.h>
|
|
#include <dirent.h>
|
|
#include <fnmatch.h>
|
|
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <sstream>
|
|
|
|
#ifdef _USING_SHORT_SERVICE_NAMES
|
|
#include <map>
|
|
#endif
|
|
|
|
#include "../../partitions.hpp"
|
|
#include "../../twrp-functions.hpp"
|
|
|
|
using namespace std;
|
|
|
|
extern "C" {
|
|
#include <cutils/properties.h>
|
|
}
|
|
|
|
#include "vold_decrypt.h"
|
|
|
|
namespace {
|
|
|
|
/* Timeouts as defined by ServiceManager */
|
|
|
|
/* The maximum amount of time to wait for a service to start or stop,
|
|
* in micro-seconds (really an approximation) */
|
|
#define SLEEP_MAX_USEC 2000000 /* 2 seconds */
|
|
/* The minimal sleeping interval between checking for the service's state
|
|
* when looping for SLEEP_MAX_USEC */
|
|
#define SLEEP_MIN_USEC 200000 /* 200 msec */
|
|
|
|
|
|
/* vold response codes defined in ResponseCode.h */
|
|
// 200 series - Requested action has been successfully completed
|
|
#define COMMAND_OKAY 200
|
|
#define PASSWORD_TYPE_RESULT 213
|
|
|
|
|
|
#define LOGINFO(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]I:" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
|
|
#define LOGKMSG(...) do { if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]K:" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
|
|
#define LOGERROR(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]E:" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
|
|
|
|
FILE *fp_kmsg = NULL;
|
|
int sdkver = 20;
|
|
|
|
|
|
/* Debugging Functions */
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
|
|
|
|
#ifndef VD_STRACE_BIN
|
|
#define VD_STRACE_BIN "/system/bin/strace"
|
|
#endif
|
|
|
|
bool has_strace = false;
|
|
pid_t pid_strace = 0;
|
|
|
|
void Strace_init_Start(void) {
|
|
has_strace = TWFunc::Path_Exists(VD_STRACE_BIN);
|
|
if (!has_strace) {
|
|
LOGINFO("strace binary (%s) not found, disabling strace in vold_decrypt!\n", VD_STRACE_BIN);
|
|
return;
|
|
}
|
|
|
|
pid_t pid;
|
|
switch(pid = fork())
|
|
{
|
|
case -1:
|
|
LOGKMSG("forking strace_init failed: %d (%s)!\n", errno, strerror(errno));
|
|
return;
|
|
case 0: // child
|
|
execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", "/tmp/strace_init.log", "-p", "1" , NULL);
|
|
LOGKMSG("strace_init fork failed: %d (%s)!\n", errno, strerror(errno));
|
|
exit(-1);
|
|
default:
|
|
LOGKMSG("Starting strace_init (pid=%d)\n", pid);
|
|
pid_strace = pid;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Strace_init_Stop(void) {
|
|
if (pid_strace > 0) {
|
|
LOGKMSG("Stopping strace_init (pid=%d)\n", pid_strace);
|
|
int timeout;
|
|
int status;
|
|
pid_t retpid = waitpid(pid_strace, &status, WNOHANG);
|
|
|
|
kill(pid_strace, SIGTERM);
|
|
for (timeout = 5; retpid == 0 && timeout; --timeout) {
|
|
sleep(1);
|
|
retpid = waitpid(pid_strace, &status, WNOHANG);
|
|
}
|
|
if (retpid)
|
|
LOGKMSG("strace_init terminated successfully\n");
|
|
else {
|
|
// SIGTERM didn't work, kill it instead
|
|
kill(pid_strace, SIGKILL);
|
|
for (timeout = 5; retpid == 0 && timeout; --timeout) {
|
|
sleep(1);
|
|
retpid = waitpid(pid_strace, &status, WNOHANG);
|
|
}
|
|
if (retpid)
|
|
LOGKMSG("strace_init killed successfully\n");
|
|
else
|
|
LOGKMSG("strace_init took too long to kill, may be a zombie process\n");
|
|
}
|
|
}
|
|
}
|
|
#endif // TW_CRYPTO_SYSTEM_VOLD_DEBUG
|
|
|
|
|
|
/* Convert a binary key of specified length into an ascii hex string equivalent,
|
|
* without the leading 0x and with null termination
|
|
*
|
|
* Original code from cryptfs.c
|
|
*/
|
|
string convert_key_to_hex_ascii(const string& master_key) {
|
|
size_t i;
|
|
unsigned char nibble;
|
|
string master_key_ascii = "";
|
|
|
|
for (i = 0; i < master_key.size(); ++i) {
|
|
nibble = (master_key[i] >> 4) & 0xf;
|
|
nibble += nibble > 9 ? 0x57 : 0x30;
|
|
master_key_ascii += nibble;
|
|
|
|
nibble = master_key[i] & 0xf;
|
|
nibble += nibble > 9 ? 0x57 : 0x30;
|
|
master_key_ascii += nibble;
|
|
}
|
|
|
|
return master_key_ascii;
|
|
}
|
|
|
|
/* Helper Functions */
|
|
#define PATH_EXISTS(path) (access(path, F_OK) >= 0)
|
|
|
|
int vrename(const string& oldname, const string& newname, bool verbose = false) {
|
|
const char *old_name = oldname.c_str();
|
|
const char *new_name = newname.c_str();
|
|
|
|
if (!PATH_EXISTS(old_name))
|
|
return 0;
|
|
|
|
if (rename(old_name, new_name) < 0) {
|
|
LOGERROR("Moving %s to %s failed: %d (%s)\n", old_name, new_name, errno, strerror(errno));
|
|
return -1;
|
|
} else if (verbose)
|
|
LOGINFO("Renamed %s to %s\n", old_name, new_name);
|
|
else
|
|
LOGKMSG("Renamed %s to %s\n", old_name, new_name);
|
|
return 0;
|
|
}
|
|
|
|
int vsymlink(const string& oldname, const string& newname, bool verbose = false) {
|
|
const char *old_name = oldname.c_str();
|
|
const char *new_name = newname.c_str();
|
|
|
|
if (!PATH_EXISTS(old_name))
|
|
return 0;
|
|
|
|
if (symlink(old_name, new_name) < 0) {
|
|
LOGERROR("Symlink %s -> %s failed: %d (%s)\n", new_name, old_name, errno, strerror(errno));
|
|
return -1;
|
|
} else if (verbose)
|
|
LOGINFO("Symlinked %s -> %s\n", new_name, old_name);
|
|
else
|
|
LOGKMSG("Symlinked %s -> %s\n", new_name, old_name);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Properties and Services Functions */
|
|
string Wait_For_Property(const string& property_name, int utimeout = SLEEP_MAX_USEC, const string& expected_value = "not_empty") {
|
|
char prop_value[PROPERTY_VALUE_MAX];
|
|
|
|
if (expected_value == "not_empty") {
|
|
while (utimeout > 0) {
|
|
property_get(property_name.c_str(), prop_value, "error");
|
|
if (strcmp(prop_value, "error") != 0)
|
|
break;
|
|
LOGKMSG("waiting for %s to get set\n", property_name.c_str());
|
|
utimeout -= SLEEP_MIN_USEC;
|
|
usleep(SLEEP_MIN_USEC);;
|
|
}
|
|
}
|
|
else {
|
|
while (utimeout > 0) {
|
|
property_get(property_name.c_str(), prop_value, "error");
|
|
if (strcmp(prop_value, expected_value.c_str()) == 0)
|
|
break;
|
|
LOGKMSG("waiting for %s to change from '%s' to '%s'\n", property_name.c_str(), prop_value, expected_value.c_str());
|
|
utimeout -= SLEEP_MIN_USEC;
|
|
usleep(SLEEP_MIN_USEC);
|
|
}
|
|
}
|
|
property_get(property_name.c_str(), prop_value, "error");
|
|
|
|
return prop_value;
|
|
}
|
|
|
|
string Get_Service_State(const string& initrc_svc) {
|
|
char prop_value[PROPERTY_VALUE_MAX];
|
|
string init_svc = "init.svc." + initrc_svc;
|
|
property_get(init_svc.c_str(), prop_value, "error");
|
|
return prop_value;
|
|
}
|
|
|
|
bool Service_Exists(const string& initrc_svc) {
|
|
return (Get_Service_State(initrc_svc) != "error");
|
|
}
|
|
|
|
bool Is_Service_Running(const string& initrc_svc) {
|
|
return (Get_Service_State(initrc_svc) == "running");
|
|
}
|
|
|
|
bool Is_Service_Stopped(const string& initrc_svc) {
|
|
return (Get_Service_State(initrc_svc) == "stopped");
|
|
}
|
|
|
|
bool Start_Service(const string& initrc_svc, int utimeout = SLEEP_MAX_USEC) {
|
|
string res = "error";
|
|
string init_svc = "init.svc." + initrc_svc;
|
|
|
|
property_set("ctl.start", initrc_svc.c_str());
|
|
|
|
res = Wait_For_Property(init_svc, utimeout, "running");
|
|
|
|
LOGINFO("Start service %s: %s.\n", initrc_svc.c_str(), res.c_str());
|
|
|
|
return (res == "running");
|
|
}
|
|
|
|
bool Stop_Service(const string& initrc_svc, int utimeout = SLEEP_MAX_USEC) {
|
|
string res = "error";
|
|
|
|
if (Service_Exists(initrc_svc)) {
|
|
string init_svc = "init.svc." + initrc_svc;
|
|
property_set("ctl.stop", initrc_svc.c_str());
|
|
res = Wait_For_Property(init_svc, utimeout, "stopped");
|
|
LOGINFO("Stop service %s: %s.\n", initrc_svc.c_str(), res.c_str());
|
|
}
|
|
|
|
return (res == "stopped");
|
|
}
|
|
|
|
|
|
/* Vendor, Firmware and fstab symlink Functions */
|
|
bool is_Vendor_Mounted(void) {
|
|
static int is_mounted = -1;
|
|
if (is_mounted < 0)
|
|
is_mounted = PartitionManager.Is_Mounted_By_Path("/vendor") ? 1 : 0;
|
|
return is_mounted;
|
|
}
|
|
|
|
bool is_Firmware_Mounted(void) {
|
|
static int is_mounted = -1;
|
|
if (is_mounted < 0)
|
|
is_mounted = PartitionManager.Is_Mounted_By_Path("/firmware") ? 1 : 0;
|
|
return is_mounted;
|
|
}
|
|
|
|
bool will_VendorBin_Be_Symlinked(void) {
|
|
return (!is_Vendor_Mounted() && TWFunc::Path_Exists("/system/vendor"));
|
|
}
|
|
|
|
bool Symlink_Vendor_Folder(void) {
|
|
bool is_vendor_symlinked = false;
|
|
|
|
if (is_Vendor_Mounted()) {
|
|
LOGINFO("vendor partition mounted, skipping /vendor substitution\n");
|
|
}
|
|
else if (TWFunc::Path_Exists("/system/vendor")) {
|
|
LOGINFO("Symlinking vendor folder...\n");
|
|
if (!TWFunc::Path_Exists("/vendor") || vrename("/vendor", "/vendor-orig") == 0) {
|
|
TWFunc::Recursive_Mkdir("/vendor/firmware/keymaster");
|
|
vsymlink("/system/vendor/lib64", "/vendor/lib64", true);
|
|
vsymlink("/system/vendor/lib", "/vendor/lib", true);
|
|
vsymlink("/system/vendor/bin", "/vendor/bin", true);
|
|
is_vendor_symlinked = true;
|
|
property_set("vold_decrypt.symlinked_vendor", "1");
|
|
}
|
|
}
|
|
return is_vendor_symlinked;
|
|
}
|
|
|
|
void Restore_Vendor_Folder(void) {
|
|
property_set("vold_decrypt.symlinked_vendor", "0");
|
|
TWFunc::removeDir("/vendor", false);
|
|
vrename("/vendor-orig", "/vendor");
|
|
}
|
|
|
|
bool Symlink_Firmware_Folder(void) {
|
|
bool is_firmware_symlinked = false;
|
|
|
|
if (is_Firmware_Mounted()) {
|
|
LOGINFO("firmware partition mounted, skipping /firmware substitution\n");
|
|
}
|
|
else {
|
|
LOGINFO("Symlinking firmware folder...\n");
|
|
if (!TWFunc::Path_Exists("/firmware") || vrename("/firmware", "/firmware-orig") == 0) {
|
|
TWFunc::Recursive_Mkdir("/firmware/image");
|
|
is_firmware_symlinked = true;
|
|
property_set("vold_decrypt.symlinked_firmware", "1");
|
|
}
|
|
}
|
|
return is_firmware_symlinked;
|
|
}
|
|
|
|
void Restore_Firmware_Folder(void) {
|
|
property_set("vold_decrypt.symlinked_firmware", "0");
|
|
TWFunc::removeDir("/firmware", false);
|
|
vrename("/firmware-orig", "/firmware");
|
|
}
|
|
|
|
int Find_Firmware_Files(const string& Path, vector<string> *FileList) {
|
|
int ret;
|
|
DIR* d;
|
|
struct dirent* de;
|
|
string FileName;
|
|
|
|
d = opendir(Path.c_str());
|
|
if (d == NULL) {
|
|
closedir(d);
|
|
return -1;
|
|
}
|
|
while ((de = readdir(d)) != NULL) {
|
|
if (de->d_type == DT_DIR) {
|
|
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
|
|
continue;
|
|
FileName = Path + "/" + de->d_name;
|
|
ret = Find_Firmware_Files(FileName, FileList);
|
|
if (ret < 0)
|
|
return -1;
|
|
} else if (de->d_type == DT_REG) {
|
|
if (fnmatch("keymaste*.*", de->d_name, 0) == 0 || fnmatch("cmnlib.*", de->d_name, 0) == 0) {
|
|
FileName = Path + "/" + de->d_name;
|
|
FileList->push_back(FileName);
|
|
}
|
|
}
|
|
}
|
|
closedir(d);
|
|
return 0;
|
|
}
|
|
|
|
void Symlink_Firmware_Files(bool is_vendor_symlinked, bool is_firmware_symlinked) {
|
|
if (!is_vendor_symlinked && !is_firmware_symlinked)
|
|
return;
|
|
|
|
LOGINFO("Symlinking firmware files...\n");
|
|
|
|
vector<string> FirmwareFiles;
|
|
Find_Firmware_Files("/system", &FirmwareFiles);
|
|
|
|
for (size_t i = 0; i < FirmwareFiles.size(); ++i) {
|
|
string base_name = TWFunc::Get_Filename(FirmwareFiles[i]);
|
|
|
|
if (is_firmware_symlinked)
|
|
vsymlink(FirmwareFiles[i], "/firmware/image/" + base_name);
|
|
|
|
if (is_vendor_symlinked) {
|
|
vsymlink(FirmwareFiles[i], "/vendor/firmware/" + base_name);
|
|
vsymlink(FirmwareFiles[i], "/vendor/firmware/keymaster/" + base_name);
|
|
}
|
|
}
|
|
LOGINFO("%d file(s) symlinked.\n", (int)FirmwareFiles.size());
|
|
}
|
|
|
|
// Android 8.0 fs_mgr checks for "/system/bin/recovery", in which case it will
|
|
// use /etc/recovery.fstab -> symlink it temporarily. Reference:
|
|
// https://android.googlesource.com/platform/system/core/+/android-8.0.0_r17/fs_mgr/fs_mgr_fstab.cpp#693
|
|
bool Symlink_Recovery_Fstab(void) {
|
|
bool is_fstab_symlinked = false;
|
|
|
|
if (vrename("/etc/recovery.fstab", "/etc/recovery-fstab-orig") == 0) {
|
|
is_fstab_symlinked = true;
|
|
|
|
// now attempt to symlink to /fstab.{ro.hardware}, but even if that
|
|
// fails, keep TWRP's fstab hidden since it cannot be parsed by fs_mgr
|
|
char prop_value[PROPERTY_VALUE_MAX];
|
|
property_get("ro.hardware", prop_value, "error");
|
|
if (strcmp(prop_value, "error")) {
|
|
string fstab_device = "/fstab."; fstab_device += prop_value;
|
|
vsymlink(fstab_device, "/etc/recovery.fstab");
|
|
}
|
|
}
|
|
return is_fstab_symlinked;
|
|
}
|
|
|
|
void Restore_Recovery_Fstab(void) {
|
|
unlink("/etc/recovery.fstab");
|
|
vrename("/etc/recovery-fstab-orig", "/etc/recovery.fstab");
|
|
}
|
|
|
|
|
|
/* Additional Services Functions */
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
|
|
typedef struct {
|
|
string Service_Name;
|
|
string Service_Path;
|
|
string Service_Binary;
|
|
|
|
string VOLD_Service_Name;
|
|
string TWRP_Service_Name;
|
|
bool is_running;
|
|
bool resume;
|
|
bool bin_exists;
|
|
bool svc_exists;
|
|
} AdditionalService;
|
|
|
|
typedef struct {
|
|
string Service_Name;
|
|
string Service_Path;
|
|
string Service_Binary;
|
|
} RC_Service;
|
|
|
|
// expand_props() courtesy of platform_system_core_init_util.cpp
|
|
bool expand_props(const std::string& src, std::string* dst) {
|
|
const char* src_ptr = src.c_str();
|
|
|
|
if (!dst) {
|
|
return false;
|
|
}
|
|
|
|
/* - variables can either be $x.y or ${x.y}, in case they are only part
|
|
* of the string.
|
|
* - will accept $$ as a literal $.
|
|
* - no nested property expansion, i.e. ${foo.${bar}} is not supported,
|
|
* bad things will happen
|
|
* - ${x.y:-default} will return default value if property empty.
|
|
*/
|
|
while (*src_ptr) {
|
|
const char* c;
|
|
|
|
c = strchr(src_ptr, '$');
|
|
if (!c) {
|
|
dst->append(src_ptr);
|
|
return true;
|
|
}
|
|
|
|
dst->append(src_ptr, c);
|
|
c++;
|
|
|
|
if (*c == '$') {
|
|
dst->push_back(*(c++));
|
|
src_ptr = c;
|
|
continue;
|
|
} else if (*c == '\0') {
|
|
return true;
|
|
}
|
|
|
|
std::string prop_name;
|
|
std::string def_val;
|
|
if (*c == '{') {
|
|
c++;
|
|
const char* end = strchr(c, '}');
|
|
if (!end) {
|
|
// failed to find closing brace, abort.
|
|
return false;
|
|
}
|
|
prop_name = std::string(c, end);
|
|
c = end + 1;
|
|
size_t def = prop_name.find(":-");
|
|
if (def < prop_name.size()) {
|
|
def_val = prop_name.substr(def + 2);
|
|
prop_name = prop_name.substr(0, def);
|
|
}
|
|
} else {
|
|
prop_name = c;
|
|
c += prop_name.size();
|
|
}
|
|
|
|
if (prop_name.empty()) {
|
|
return false;
|
|
}
|
|
|
|
char prop_value[PROPERTY_VALUE_MAX];
|
|
property_get(prop_name.c_str(), prop_value, "");
|
|
std::string prop_val = prop_value;
|
|
if (prop_val.empty()) {
|
|
if (def_val.empty()) {
|
|
return false;
|
|
}
|
|
prop_val = def_val;
|
|
}
|
|
|
|
dst->append(prop_val);
|
|
src_ptr = c;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
string GetArgument(const string& line, size_t argument_number, bool expand_properties) {
|
|
size_t beg;
|
|
size_t end;
|
|
string argument;
|
|
|
|
beg = line.find_first_not_of(" \t\r");
|
|
if (beg == string::npos)
|
|
return "";
|
|
|
|
for (size_t i = 0; i < argument_number; ++i) {
|
|
end = line.find_first_of(" \t\r", beg);
|
|
if (end == string::npos)
|
|
return "";
|
|
|
|
beg = line.find_first_not_of(" \t\r", end);
|
|
if (beg == string::npos)
|
|
return "";
|
|
}
|
|
|
|
end = line.find_first_of(" \t\r", beg);
|
|
if (end == string::npos)
|
|
argument = line.substr(beg);
|
|
else
|
|
argument = line.substr(beg, end - beg); // exclude trailing whitespace
|
|
|
|
if (expand_properties) {
|
|
string expanded_property_argument;
|
|
if (expand_props(argument, &expanded_property_argument))
|
|
return expanded_property_argument;
|
|
else
|
|
return "";
|
|
} else {
|
|
return argument;
|
|
}
|
|
}
|
|
|
|
// Very simplified .rc parser to get services
|
|
void Parse_RC_File(const string& rc_file, vector<RC_Service>& RC_Services) {
|
|
ifstream file;
|
|
|
|
file.open(rc_file.c_str(), ios::in);
|
|
if (!file.is_open())
|
|
return;
|
|
|
|
size_t beg; // left trim
|
|
size_t end; // right trim
|
|
bool continuation = false; // backslash continuation
|
|
string line; // line
|
|
string real_line; // trimmed line with backslash continuation removal
|
|
vector<string> imports; // file names of imports (we don't want to recursively do while the file is open)
|
|
|
|
while (getline(file, line)) {
|
|
beg = line.find_first_not_of(" \t\r");
|
|
end = line.find_last_not_of(" \t\r");
|
|
if (end == string::npos)
|
|
end = line.length();
|
|
|
|
if (beg == string::npos) {
|
|
if (continuation)
|
|
continuation = false;
|
|
else
|
|
continue;
|
|
} else if (line[end] == '\\') {
|
|
continuation = true;
|
|
real_line += line.substr(beg, end - beg); // excluding backslash
|
|
continue;
|
|
} else if (continuation) {
|
|
continuation = false;
|
|
real_line += line.substr(beg, end - beg + 1);
|
|
} else {
|
|
real_line = line.substr(beg, end - beg + 1);
|
|
}
|
|
|
|
if (GetArgument(real_line, 0, false) == "import") {
|
|
// handle: import <file>
|
|
string file_name = GetArgument(real_line, 1, true);
|
|
if (file_name.empty()) {
|
|
// INVALID IMPORT
|
|
} else
|
|
imports.push_back(file_name);
|
|
} else if (GetArgument(real_line, 0, false) == "service") {
|
|
// handle: service <name> <path>
|
|
RC_Service svc;
|
|
|
|
svc.Service_Name = GetArgument(real_line, 1, false);
|
|
svc.Service_Path = GetArgument(real_line, 2, true);
|
|
|
|
if (svc.Service_Name.empty() || svc.Service_Path.empty()) {
|
|
// INVALID SERVICE ENTRY
|
|
} else {
|
|
beg = svc.Service_Path.find_last_of("/");
|
|
if (beg == string::npos)
|
|
svc.Service_Binary = svc.Service_Path;
|
|
else
|
|
svc.Service_Binary = svc.Service_Path.substr(beg + 1);
|
|
|
|
/*
|
|
#ifdef _USING_SHORT_SERVICE_NAMES
|
|
if (svc.Service_Name.length() > 16) {
|
|
LOGERROR("WARNING: Ignoring service %s (-> %s)\n"
|
|
" defined in %s is greater than 16 characters and will\n"
|
|
" not be able to be run by init on android-7.1 or below!\n",
|
|
svc.Service_Name.c_str(), svc.Service_Path.c_str(), rc_file.c_str()
|
|
);
|
|
}
|
|
else
|
|
#endif
|
|
*/
|
|
RC_Services.push_back(svc);
|
|
}
|
|
}
|
|
real_line.clear();
|
|
}
|
|
file.close();
|
|
|
|
for (size_t i = 0; i < imports.size(); ++i) {
|
|
Parse_RC_File(imports[i], RC_Services);
|
|
}
|
|
}
|
|
|
|
vector<AdditionalService> Get_List_Of_Additional_Services(void) {
|
|
vector<AdditionalService> services;
|
|
|
|
// Additional Services needed by vold_decrypt (eg qseecomd, hwservicemanager, etc)
|
|
vector<string> service_names = TWFunc::Split_String(TW_CRYPTO_SYSTEM_VOLD_SERVICES, " ");
|
|
for (size_t i = 0; i < service_names.size(); ++i) {
|
|
AdditionalService svc;
|
|
svc.Service_Name = service_names[i];
|
|
svc.bin_exists = false;
|
|
svc.svc_exists = false;
|
|
services.push_back(svc);
|
|
|
|
#ifdef _USING_SHORT_SERVICE_NAMES
|
|
// Fallback code for >16 character service names which
|
|
// allows for multiple definitions in custom .rc files
|
|
if (service_names[i].length() > 12) {
|
|
svc.Service_Name = service_names[i].substr(0, 12); // 16-4(prefix)=12
|
|
svc.bin_exists = false;
|
|
svc.svc_exists = false;
|
|
services.push_back(svc);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Read list of all services defined in all .rc files
|
|
vector<RC_Service> RC_Services;
|
|
Parse_RC_File("/init.rc", RC_Services);
|
|
|
|
|
|
// Cross reference Additional Services against the .rc Services and establish
|
|
// availability of the binaries, otherwise disable it to avoid unnecessary
|
|
// delays and log spam.
|
|
// Also check for duplicate entries between TWRP and vold_decrypt so we can
|
|
// stop and restart any conflicting services.
|
|
for (size_t i = 0; i < RC_Services.size(); ++i) {
|
|
string prefix = RC_Services[i].Service_Name.substr(0, 4);
|
|
|
|
#ifdef _USING_SHORT_SERVICE_NAMES
|
|
map<string,size_t> rc_indeces;
|
|
#endif
|
|
|
|
if (prefix != "sys_" && prefix != "ven_") {
|
|
#ifdef _USING_SHORT_SERVICE_NAMES
|
|
if (RC_Services[i].Service_Name.length() > 12) {
|
|
// save this entry for potential binary name match
|
|
rc_indeces.insert(pair<string,size_t>(RC_Services[i].Service_Binary, i));
|
|
}
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
for (size_t j = 0; j < services.size(); ++j) {
|
|
string path = RC_Services[i].Service_Path;
|
|
if (prefix == "ven_" && will_VendorBin_Be_Symlinked()) {
|
|
path = "/system" + path; // vendor is going to get symlinked to /system/vendor
|
|
}
|
|
|
|
if (RC_Services[i].Service_Name == prefix + services[j].Service_Name) {
|
|
services[j].svc_exists = true;
|
|
|
|
if (!services[j].VOLD_Service_Name.empty() && TWFunc::Path_Exists(path)) {
|
|
// Duplicate match, log but use previous definition
|
|
LOGERROR("Service %s: VOLD_Service_Name already defined as %s\n", RC_Services[i].Service_Name.c_str(), services[j].VOLD_Service_Name.c_str());
|
|
}
|
|
else if (TWFunc::Path_Exists(path)) {
|
|
services[j].bin_exists = true;
|
|
services[j].VOLD_Service_Name = RC_Services[i].Service_Name; // prefix + service_name
|
|
services[j].Service_Path = RC_Services[i].Service_Path;
|
|
services[j].Service_Binary = RC_Services[i].Service_Binary;
|
|
|
|
if (Service_Exists(services[j].Service_Name))
|
|
services[j].TWRP_Service_Name = services[j].Service_Name;
|
|
else if (Service_Exists("system/bin" + services[j].Service_Name))
|
|
services[j].TWRP_Service_Name = "system/bin" + services[j].Service_Name;
|
|
else
|
|
services[j].TWRP_Service_Name.clear();
|
|
|
|
#ifdef _USING_SHORT_SERVICE_NAMES
|
|
if (services[j].TWRP_Service_Name.empty()) {
|
|
// Try matching Service_Binary (due to 16 character service_name limit in 7.1 and below)
|
|
map<string,size_t>::iterator it = rc_indeces.find(services[j].Service_Binary);
|
|
if (it != rc_indeces.end()) {
|
|
services[j].TWRP_Service_Name = RC_Services[it->second].Service_Name;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LOGINFO("List of additional services for vold_decrypt:\n");
|
|
for (size_t i = 0; i < services.size(); ++i) {
|
|
if (!services[i].svc_exists) {
|
|
LOGINFO(" %s: Disabled due to lack of .rc service entry\n", services[i].Service_Name.c_str());
|
|
} else if (services[i].bin_exists) {
|
|
if (services[i].TWRP_Service_Name.empty()) {
|
|
LOGINFO(" %s: Enabled as %s -> %s\n",
|
|
services[i].Service_Name.c_str(),
|
|
services[i].VOLD_Service_Name.c_str(), services[i].Service_Path.c_str()
|
|
);
|
|
} else {
|
|
LOGINFO(" %s: Enabled as %s -> %s (temporarily replacing TWRP's %s service)\n",
|
|
services[i].Service_Name.c_str(),
|
|
services[i].VOLD_Service_Name.c_str(), services[i].Service_Path.c_str(),
|
|
services[i].TWRP_Service_Name.c_str()
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
LOGINFO(" %s: Disabled due to lack of matching binary\n", services[i].Service_Name.c_str());
|
|
}
|
|
}
|
|
return services;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Misc Functions */
|
|
void Set_Needed_Properties(void) {
|
|
// vold won't start without ro.storage_structure on Kitkat
|
|
string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk");
|
|
if (!sdkverstr.empty()) {
|
|
sdkver = atoi(sdkverstr.c_str());
|
|
}
|
|
if (sdkver <= 19) {
|
|
string ro_storage_structure = TWFunc::System_Property_Get("ro.storage_structure");
|
|
if (!ro_storage_structure.empty())
|
|
property_set("ro.storage_structure", ro_storage_structure.c_str());
|
|
}
|
|
property_set("hwservicemanager.ready", "false");
|
|
property_set("sys.listeners.registered", "false");
|
|
property_set("vendor.sys.listeners.registered", "false");
|
|
}
|
|
|
|
void Update_Patch_Level(void) {
|
|
// On Oreo and above, keymaster requires Android version & patch level to match installed system
|
|
string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk");
|
|
if (!sdkverstr.empty()) {
|
|
sdkver = atoi(sdkverstr.c_str());
|
|
}
|
|
if (sdkver <= 25) {
|
|
property_set("vold_decrypt.legacy_system", "true");
|
|
} else {
|
|
LOGINFO("Current system is Oreo or above. Setting OS version and security patch level from installed system...\n");
|
|
property_set("vold_decrypt.legacy_system", "false");
|
|
}
|
|
|
|
char prop_value[PROPERTY_VALUE_MAX];
|
|
char legacy_system_value[PROPERTY_VALUE_MAX] = "false";
|
|
property_get("vold_decrypt.legacy_system", prop_value, "");
|
|
|
|
// Only set OS ver and patch level if device uses Oreo+ system
|
|
if (strcmp(prop_value, legacy_system_value) == 0) {
|
|
property_get("ro.build.version.release", prop_value, "");
|
|
std::string osver_orig = prop_value;
|
|
property_set("vold_decrypt.osver_orig", osver_orig.c_str());
|
|
LOGINFO("Current OS version: %s\n", osver_orig.c_str());
|
|
|
|
int error = 0;
|
|
std::string osver = TWFunc::System_Property_Get("ro.build.version.release");
|
|
if (!(osver == osver_orig)) {
|
|
if (!(error = TWFunc::Property_Override("ro.build.version.release", osver))) {
|
|
LOGINFO("Property override successful! New OS version: %s\n", osver.c_str());
|
|
} else {
|
|
LOGERROR("Property override failed, code %d\n", error);
|
|
return;
|
|
}
|
|
// TODO: Confirm whether we actually need to update the props in prop.default
|
|
std::string sed_osver = "sed -i 's/ro.build.version.release=.*/ro.build.version.release=" + osver + "/g' /prop.default";
|
|
TWFunc::Exec_Cmd(sed_osver);
|
|
property_set("vold_decrypt.osver_set", "true");
|
|
} else {
|
|
LOGINFO("Current OS version & System OS version already match. Proceeding to next step.\n");
|
|
property_set("vold_decrypt.osver_set", "false");
|
|
}
|
|
|
|
property_get("ro.build.version.security_patch", prop_value, "");
|
|
std::string patchlevel_orig = prop_value;
|
|
property_set("vold_decrypt.patchlevel_orig", patchlevel_orig.c_str());
|
|
LOGINFO("Current security patch level: %s\n", patchlevel_orig.c_str());
|
|
|
|
std::string patchlevel = TWFunc::System_Property_Get("ro.build.version.security_patch");
|
|
if (!(patchlevel == patchlevel_orig)) {
|
|
if (!(error = TWFunc::Property_Override("ro.build.version.security_patch", patchlevel))) {
|
|
LOGINFO("Property override successful! New security patch level: %s\n", patchlevel.c_str());
|
|
} else {
|
|
LOGERROR("Property override failed, code %d\n", error);
|
|
return;
|
|
}
|
|
// TODO: Confirm whether we actually need to update the props in prop.default
|
|
std::string sed_patchlevel = "sed -i 's/ro.build.version.security_patch=.*/ro.build.version.security_patch=" + patchlevel + "/g' /prop.default";
|
|
TWFunc::Exec_Cmd(sed_patchlevel);
|
|
property_set("vold_decrypt.patchlevel_set", "true");
|
|
} else {
|
|
LOGINFO("Current security patch level & System security patch level already match. Proceeding to next step.\n");
|
|
property_set("vold_decrypt.patchlevel_set", "false");
|
|
}
|
|
return;
|
|
} else {
|
|
LOGINFO("Current system is Nougat or older. Skipping OS version and security patch level setting...\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Revert_Patch_Level(void) {
|
|
char osver_set[PROPERTY_VALUE_MAX];
|
|
char patchlevel_set[PROPERTY_VALUE_MAX];
|
|
char osver_patchlevel_set[PROPERTY_VALUE_MAX] = "false";
|
|
|
|
property_get("vold_decrypt.osver_set", osver_set, "");
|
|
property_get("vold_decrypt.patchlevel_set", patchlevel_set, "");
|
|
|
|
int osver_result = strcmp(osver_set, osver_patchlevel_set);
|
|
int patchlevel_result = strcmp(patchlevel_set, osver_patchlevel_set);
|
|
if (!(osver_result == 0 && patchlevel_result == 0)) {
|
|
char prop_value[PROPERTY_VALUE_MAX];
|
|
LOGINFO("Reverting OS version and security patch level to original TWRP values...\n");
|
|
property_get("vold_decrypt.osver_orig", prop_value, "");
|
|
std::string osver_orig = prop_value;
|
|
property_get("ro.build.version.release", prop_value, "");
|
|
std::string osver = prop_value;
|
|
|
|
int error = 0;
|
|
if (!(osver == osver_orig)) {
|
|
if (!(error = TWFunc::Property_Override("ro.build.version.release", osver_orig))) {
|
|
LOGINFO("Property override successful! Original OS version: %s\n", osver_orig.c_str());
|
|
} else {
|
|
LOGERROR("Property override failed, code %d\n", error);
|
|
return;
|
|
}
|
|
// TODO: Confirm whether we actually need to update the props in prop.default
|
|
std::string sed_osver_orig = "sed -i 's/ro.build.version.release=.*/ro.build.version.release=" + osver_orig + "/g' /prop.default";
|
|
TWFunc::Exec_Cmd(sed_osver_orig);
|
|
}
|
|
|
|
property_get("vold_decrypt.patchlevel_orig", prop_value, "");
|
|
std::string patchlevel_orig = prop_value;
|
|
property_get("ro.build.version.security_patch", prop_value, "");
|
|
std::string patchlevel = prop_value;
|
|
|
|
if (!(patchlevel == patchlevel_orig)) {
|
|
if (!(error = TWFunc::Property_Override("ro.build.version.security_patch", patchlevel_orig))) {
|
|
LOGINFO("Property override successful! Original security patch level: %s\n", patchlevel_orig.c_str());
|
|
} else {
|
|
LOGERROR("Property override failed, code %d\n", error);
|
|
return;
|
|
}
|
|
// TODO: Confirm whether we actually need to update the props in prop.default
|
|
std::string sed_patchlevel_orig = "sed -i 's/ro.build.version.security_patch=.*/ro.build.version.security_patch=" + patchlevel_orig + "/g' /prop.default";
|
|
TWFunc::Exec_Cmd(sed_patchlevel_orig);
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
static unsigned int get_blkdev_size(int fd) {
|
|
unsigned long nr_sec;
|
|
|
|
if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
|
|
nr_sec = 0;
|
|
}
|
|
|
|
return (unsigned int) nr_sec;
|
|
}
|
|
|
|
#define CRYPT_FOOTER_OFFSET 0x4000
|
|
static char footer[16 * 1024];
|
|
const char* userdata_path;
|
|
static off64_t offset;
|
|
|
|
int footer_br(const string& command) {
|
|
int fd;
|
|
|
|
if (command == "backup") {
|
|
unsigned int nr_sec;
|
|
TWPartition* userdata = PartitionManager.Find_Partition_By_Path("/data");
|
|
userdata_path = userdata->Actual_Block_Device.c_str();
|
|
fd = open(userdata_path, O_RDONLY);
|
|
if (fd < 0) {
|
|
LOGERROR("E:footer_backup: Cannot open '%s': %s\n", userdata_path, strerror(errno));
|
|
return -1;
|
|
}
|
|
if ((nr_sec = get_blkdev_size(fd))) {
|
|
offset = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
|
|
} else {
|
|
LOGERROR("E:footer_br: Failed to get offset\n");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
if (lseek64(fd, offset, SEEK_SET) == -1) {
|
|
LOGERROR("E:footer_backup: Failed to lseek64\n");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
if (read(fd, footer, sizeof(footer)) != sizeof(footer)) {
|
|
LOGERROR("E:footer_br: Failed to read: %s\n", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
} else if (command == "restore") {
|
|
fd = open(userdata_path, O_WRONLY);
|
|
if (fd < 0) {
|
|
LOGERROR("E:footer_restore: Cannot open '%s': %s\n", userdata_path, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (lseek64(fd, offset, SEEK_SET) == -1) {
|
|
LOGERROR("E:footer_restore: Failed to lseek64\n");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
if (write(fd, footer, sizeof(footer)) != sizeof(footer)) {
|
|
LOGERROR("E:footer_br: Failed to write: %s\n", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
} else {
|
|
LOGERROR("E:footer_br: wrong command argument: %s\n", command.c_str());
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* vdc Functions */
|
|
typedef struct {
|
|
string Output; // Entire line excluding \n
|
|
int ResponseCode; // ResponseCode.h (int)
|
|
int Sequence; // Sequence (int)
|
|
int Message; // Message (string) but we're only interested in int
|
|
} vdc_ReturnValues;
|
|
|
|
int Exec_vdc_cryptfs(const string& command, const string& argument, vdc_ReturnValues* vdcResult) {
|
|
pid_t pid;
|
|
int status;
|
|
int pipe_fd[2][2];
|
|
|
|
vdcResult->Output.clear();
|
|
vdcResult->ResponseCode = vdcResult->Sequence = vdcResult->Message = -1;
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
if (pipe(pipe_fd[i])) {
|
|
LOGERROR("exec_vdc_cryptfs: pipe() error!\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// getpwtype and checkpw commands are removed from Pie vdc, using modified vdc_pie
|
|
const char *cmd[] = { "/system/bin/vdc_pie", "cryptfs" };
|
|
if (sdkver < 28)
|
|
cmd[0] = "/system/bin/vdc";
|
|
const char *env[] = { "LD_LIBRARY_PATH=/system/lib64:/system/lib", NULL };
|
|
|
|
LOGINFO("sdkver: %d, using %s\n", sdkver, cmd[0]);
|
|
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
|
|
string log_name = "/tmp/strace_vdc_" + command;
|
|
#endif
|
|
|
|
switch(pid = fork())
|
|
{
|
|
case -1:
|
|
LOGERROR("exec_vdc_cryptfs: fork failed: %d (%s)!\n", errno, strerror(errno));
|
|
return -1;
|
|
|
|
case 0: // child
|
|
fflush(stdout); fflush(stderr);
|
|
for (int i = 0; i < 2; ++i) {
|
|
close(pipe_fd[i][0]);
|
|
dup2(pipe_fd[i][1], ((i == 0) ? STDOUT_FILENO : STDERR_FILENO));
|
|
close(pipe_fd[i][1]);
|
|
}
|
|
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
|
|
if (has_strace) {
|
|
if (argument.empty())
|
|
execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", log_name.c_str(),
|
|
"-E", env[0], cmd[0], cmd[1], command.c_str(), NULL);
|
|
else
|
|
execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", log_name.c_str(),
|
|
"-E", env[0], cmd[0], cmd[1], command.c_str(), argument.c_str(), NULL);
|
|
} else
|
|
#endif
|
|
if (argument.empty())
|
|
execle(cmd[0], cmd[0], cmd[1], command.c_str(), NULL, env);
|
|
else
|
|
execle(cmd[0], cmd[0], cmd[1], command.c_str(), argument.c_str(), NULL, env);
|
|
_exit(127);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
int timeout = 30*100;
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
close(pipe_fd[i][1]);
|
|
|
|
// Non-blocking read loop with timeout
|
|
int flags = fcntl(pipe_fd[i][0], F_GETFL, 0);
|
|
fcntl(pipe_fd[i][0], F_SETFL, flags | O_NONBLOCK);
|
|
}
|
|
|
|
char buffer[128];
|
|
ssize_t count;
|
|
string strout[2];
|
|
pid_t retpid = waitpid(pid, &status, WNOHANG);
|
|
while (true) {
|
|
for (int i = 0; i < 2; ++i) {
|
|
count = read(pipe_fd[i][0], buffer, sizeof(buffer));
|
|
if (count == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
else if (errno != EAGAIN)
|
|
LOGERROR("exec_vdc_cryptfs: read() error %d (%s)\n!", errno, strerror(errno));
|
|
} else if (count > 0) {
|
|
strout[i].append(buffer, count);
|
|
}
|
|
}
|
|
|
|
retpid = waitpid(pid, &status, WNOHANG);
|
|
if (retpid == 0 && --timeout)
|
|
usleep(10000);
|
|
else
|
|
break;
|
|
};
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
close(pipe_fd[i][0]);
|
|
}
|
|
|
|
if (!strout[0].empty()) {
|
|
sscanf(strout[0].c_str(), "%d %d %d", &vdcResult->ResponseCode, &vdcResult->Sequence, &vdcResult->Message);
|
|
vdcResult->Output = "I:" + strout[0];
|
|
}
|
|
if (!strout[1].empty()) {
|
|
vdcResult->Output += "E:" + strout[1];
|
|
}
|
|
std::replace(vdcResult->Output.begin(), vdcResult->Output.end(), '\n', '|');
|
|
|
|
if (!vdcResult->Output.empty() && vdcResult->Output[vdcResult->Output.length() - 1] != '|')
|
|
vdcResult->Output += "|";
|
|
vdcResult->Output += "RC=" + TWFunc::to_string(WEXITSTATUS(status));
|
|
|
|
// Error handling
|
|
if (retpid == 0 && timeout == 0) {
|
|
LOGERROR("exec_vdc_cryptfs: took too long, killing process\n");
|
|
kill(pid, SIGKILL);
|
|
for (timeout = 5; retpid == 0 && timeout; --timeout) {
|
|
sleep(1);
|
|
retpid = waitpid(pid, &status, WNOHANG);
|
|
}
|
|
if (retpid)
|
|
LOGINFO("exec_vdc_cryptfs: process killed successfully\n");
|
|
else
|
|
LOGERROR("exec_vdc_cryptfs: process took too long to kill, may be a zombie process\n");
|
|
return VD_ERR_VOLD_OPERATION_TIMEDOUT;
|
|
} else if (retpid > 0) {
|
|
if (WIFSIGNALED(status)) {
|
|
LOGERROR("exec_vdc_cryptfs: process ended with signal: %d\n", WTERMSIG(status)); // Seg fault or some other non-graceful termination
|
|
return -1;
|
|
}
|
|
} else if (retpid < 0) { // no PID returned
|
|
if (errno == ECHILD)
|
|
LOGINFO("exec_vdc_cryptfs: no child process exist\n");
|
|
else {
|
|
LOGERROR("exec_vdc_cryptfs: Unexpected error %d (%s)\n", errno, strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
if (sdkver >= 28) {
|
|
return WEXITSTATUS(status);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int Run_vdc(const string& Password) {
|
|
int res;
|
|
struct timeval t1, t2;
|
|
vdc_ReturnValues vdcResult;
|
|
|
|
LOGINFO("About to run vdc...\n");
|
|
|
|
// Pie vdc communicates with vold directly, no socket so lets not waste time
|
|
if (sdkver < 28) {
|
|
// Wait for vold connection
|
|
gettimeofday(&t1, NULL);
|
|
t2 = t1;
|
|
while ((t2.tv_sec - t1.tv_sec) < 5) {
|
|
// cryptfs getpwtype returns: R1=213(PasswordTypeResult) R2=? R3="password", "pattern", "pin", "default"
|
|
res = Exec_vdc_cryptfs("getpwtype", "", &vdcResult);
|
|
if (vdcResult.ResponseCode == PASSWORD_TYPE_RESULT) {
|
|
res = 0;
|
|
break;
|
|
}
|
|
LOGINFO("Retrying connection to vold (Reason: %s)\n", vdcResult.Output.c_str());
|
|
usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts
|
|
gettimeofday(&t2, NULL);
|
|
}
|
|
|
|
if (res == 0 && (t2.tv_sec - t1.tv_sec) < 5)
|
|
LOGINFO("Connected to vold: %s\n", vdcResult.Output.c_str());
|
|
else if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
|
|
return VD_ERR_VOLD_OPERATION_TIMEDOUT; // should never happen for getpwtype
|
|
else if (res)
|
|
return VD_ERR_FORK_EXECL_ERROR;
|
|
else if (vdcResult.ResponseCode != -1)
|
|
return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
|
|
else
|
|
return VD_ERR_VDC_FAILED_TO_CONNECT;
|
|
}
|
|
|
|
// Input password from GUI, or default password
|
|
res = Exec_vdc_cryptfs("checkpw", Password, &vdcResult);
|
|
if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
|
|
return VD_ERR_VOLD_OPERATION_TIMEDOUT;
|
|
else if (res)
|
|
return VD_ERR_FORK_EXECL_ERROR;
|
|
|
|
LOGINFO("vdc cryptfs result (passwd): %s\n", vdcResult.Output.c_str());
|
|
/*
|
|
if (res == 0 && vdcResult.ResponseCode != COMMAND_OKAY)
|
|
return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
|
|
*/
|
|
|
|
// our vdc returns vold binder op status,
|
|
// we care about status.ok() only which is 0
|
|
if (sdkver >= 28) {
|
|
vdcResult.Message = res;
|
|
}
|
|
|
|
if (vdcResult.Message != 0) {
|
|
// try falling back to Lollipop hex passwords
|
|
string hexPassword = convert_key_to_hex_ascii(Password);
|
|
res = Exec_vdc_cryptfs("checkpw", hexPassword, &vdcResult);
|
|
if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
|
|
return VD_ERR_VOLD_OPERATION_TIMEDOUT;
|
|
else if (res)
|
|
return VD_ERR_FORK_EXECL_ERROR;
|
|
|
|
LOGINFO("vdc cryptfs result (hex_pw): %s\n", vdcResult.Output.c_str());
|
|
/*
|
|
if (res == 0 && vdcResult.ResponseCode != COMMAND_OKAY)
|
|
return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
|
|
*/
|
|
}
|
|
|
|
// sdk < 28 vdc's return value is dependant upon source origin, it will either
|
|
// return 0 or ResponseCode, so disregard and focus on decryption instead
|
|
// our vdc always returns 0 on success
|
|
if (vdcResult.Message == 0) {
|
|
// Decryption successful wait for crypto blk dev
|
|
Wait_For_Property("ro.crypto.fs_crypto_blkdev");
|
|
res = VD_SUCCESS;
|
|
} else if (vdcResult.ResponseCode != COMMAND_OKAY) {
|
|
res = VD_ERR_VOLD_UNEXPECTED_RESPONSE;
|
|
} else {
|
|
res = VD_ERR_DECRYPTION_FAILED;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int Vold_Decrypt_Core(const string& Password) {
|
|
int res;
|
|
bool is_vendor_symlinked = false;
|
|
bool is_firmware_symlinked = false;
|
|
bool is_fstab_symlinked = false;
|
|
bool is_vold_running = false;
|
|
|
|
if (Password.empty()) {
|
|
LOGINFO("vold_decrypt: password is empty!\n");
|
|
return VD_ERR_PASSWORD_EMPTY;
|
|
}
|
|
|
|
// Mount ANDROID_ROOT and check for vold and vdc
|
|
if (!PartitionManager.Mount_By_Path(PartitionManager.Get_Android_Root_Path(), true)) {
|
|
return VD_ERR_UNABLE_TO_MOUNT_SYSTEM;
|
|
} else if ((!TWFunc::Path_Exists("/system/bin/vold")) && (!TWFunc::Path_Exists(PartitionManager.Get_Android_Root_Path() + "/system/bin/vold"))) {
|
|
LOGINFO("ERROR: vold not found, aborting.\n");
|
|
return VD_ERR_MISSING_VOLD;
|
|
} else if ((!TWFunc::Path_Exists("/system/bin/vdc")) && (!TWFunc::Path_Exists(PartitionManager.Get_Android_Root_Path() + "/system/bin/vdc"))) {
|
|
LOGINFO("ERROR: vdc not found, aborting.\n");
|
|
return VD_ERR_MISSING_VDC;
|
|
}
|
|
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_MOUNT
|
|
vector<string> partitions = TWFunc::Split_String(TW_CRYPTO_SYSTEM_VOLD_MOUNT, " ");
|
|
for (size_t i = 0; i < partitions.size(); ++i) {
|
|
string mnt_point = "/" + partitions[i];
|
|
if(PartitionManager.Find_Partition_By_Path(mnt_point)) {
|
|
if (!PartitionManager.Mount_By_Path(mnt_point, true)) {
|
|
LOGERROR("Unable to mount %s\n", mnt_point.c_str());
|
|
return VD_ERR_UNABLE_TO_MOUNT_EXTRA;
|
|
}
|
|
LOGINFO("%s partition mounted\n", partitions[i].c_str());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
fp_kmsg = fopen("/dev/kmsg", "a");
|
|
|
|
LOGINFO("TW_CRYPTO_USE_SYSTEM_VOLD := true\n");
|
|
|
|
// just cache the result to avoid unneeded duplicates in recovery.log
|
|
LOGINFO("Checking existence of vendor and firmware partitions...\n");
|
|
is_Vendor_Mounted();
|
|
is_Firmware_Mounted();
|
|
|
|
LOGINFO("Attempting to use system's vold for decryption...\n");
|
|
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
|
|
Strace_init_Start();
|
|
#endif
|
|
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
|
|
vector<AdditionalService> Services = Get_List_Of_Additional_Services();
|
|
|
|
// Check if TWRP is running any of the services
|
|
for (size_t i = 0; i < Services.size(); ++i) {
|
|
if (!Services[i].TWRP_Service_Name.empty() && !Is_Service_Stopped(Services[i].TWRP_Service_Name)) {
|
|
Services[i].resume = true;
|
|
Stop_Service(Services[i].TWRP_Service_Name);
|
|
} else
|
|
Services[i].resume = false;
|
|
}
|
|
#endif
|
|
|
|
LOGINFO("Setting up folders and permissions...\n");
|
|
is_fstab_symlinked = Symlink_Recovery_Fstab();
|
|
is_vendor_symlinked = Symlink_Vendor_Folder();
|
|
is_firmware_symlinked = Symlink_Firmware_Folder();
|
|
Symlink_Firmware_Files(is_vendor_symlinked, is_firmware_symlinked);
|
|
|
|
Set_Needed_Properties();
|
|
#ifdef TW_INCLUDE_LIBRESETPROP
|
|
Update_Patch_Level();
|
|
#endif
|
|
|
|
// Start services needed for vold decrypt
|
|
LOGINFO("Starting services...\n");
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
|
|
for (size_t i = 0; i < Services.size(); ++i) {
|
|
if (Services[i].bin_exists) {
|
|
if (Services[i].Service_Binary.find("keymaster") != string::npos) {
|
|
Wait_For_Property("hwservicemanager.ready", 500000, "true");
|
|
LOGINFO(" hwservicemanager is ready.\n");
|
|
}
|
|
|
|
Services[i].is_running = Start_Service(Services[i].VOLD_Service_Name);
|
|
|
|
if (Services[i].Service_Binary == "qseecomd") {
|
|
if (Wait_For_Property("sys.listeners.registered", 500000, "true") == "true"
|
|
|| Wait_For_Property("vendor.sys.listeners.registered", 500000, "true") == "true")
|
|
LOGINFO(" qseecomd listeners registered.\n");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
is_vold_running = Start_Service("sys_vold");
|
|
|
|
if (is_vold_running) {
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
|
|
for (size_t i = 0; i < Services.size(); ++i) {
|
|
if (Services[i].bin_exists && !Is_Service_Running(Services[i].VOLD_Service_Name) && Services[i].resume) {
|
|
// if system_service has died restart the twrp_service
|
|
LOGINFO("%s is not running, resuming %s!\n", Services[i].VOLD_Service_Name.c_str(), Services[i].TWRP_Service_Name.c_str());
|
|
Start_Service(Services[i].TWRP_Service_Name);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Oreo and Pie vold on some devices alters footer causing
|
|
* system to ask for decryption password at next boot although
|
|
* password haven't changed so we save footer before and restore it
|
|
* after vold operations
|
|
*/
|
|
if (sdkver > 25) {
|
|
if (footer_br("backup") == 0) {
|
|
LOGINFO("footer_br: crypto footer backed up\n");
|
|
res = Run_vdc(Password);
|
|
if (footer_br("restore") == 0)
|
|
LOGINFO("footer_br: crypto footer restored\n");
|
|
else
|
|
LOGERROR("footer_br: Failed to restore crypto footer\n");
|
|
} else {
|
|
LOGERROR("footer_br: Failed to backup crypto footer, \
|
|
skipping decrypt to prevent data loss. Reboot recovery to try again...\n");
|
|
res = -1;
|
|
}
|
|
} else {
|
|
res = Run_vdc(Password);
|
|
}
|
|
|
|
if (res != 0) {
|
|
LOGINFO("Decryption failed\n");
|
|
}
|
|
} else {
|
|
LOGINFO("Failed to start vold\n");
|
|
res = VD_ERR_VOLD_FAILED_TO_START;
|
|
}
|
|
#ifdef TW_INCLUDE_LIBRESETPROP
|
|
Revert_Patch_Level();
|
|
#endif
|
|
// Stop services needed for vold decrypt so /system can be unmounted
|
|
LOGINFO("Stopping services...\n");
|
|
Stop_Service("sys_vold");
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
|
|
for (size_t i = 0; i < Services.size(); ++i) {
|
|
if (!Is_Service_Running(Services[i].VOLD_Service_Name) && Services[i].resume)
|
|
Stop_Service(Services[i].TWRP_Service_Name);
|
|
else if (Services[i].bin_exists)
|
|
Stop_Service(Services[i].VOLD_Service_Name);
|
|
}
|
|
#endif
|
|
|
|
if (is_firmware_symlinked)
|
|
Restore_Firmware_Folder();
|
|
if (is_vendor_symlinked)
|
|
Restore_Vendor_Folder();
|
|
if (is_fstab_symlinked)
|
|
Restore_Recovery_Fstab();
|
|
|
|
if (!PartitionManager.UnMount_By_Path(PartitionManager.Get_Android_Root_Path(), true)) {
|
|
// PartitionManager failed to unmount ANDROID_ROOT, this should not happen,
|
|
// but in case it does, do a lazy unmount
|
|
LOGINFO("WARNING: '%s' could not be unmounted normally!\n", PartitionManager.Get_Android_Root_Path().c_str());
|
|
umount2(PartitionManager.Get_Android_Root_Path().c_str(), MNT_DETACH);
|
|
}
|
|
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_MOUNT
|
|
for (size_t i = 0; i < partitions.size(); ++i) {
|
|
string mnt_point = "/" + partitions[i];
|
|
if(PartitionManager.Is_Mounted_By_Path(mnt_point)) {
|
|
if (!PartitionManager.UnMount_By_Path(mnt_point, true)) {
|
|
LOGINFO("WARNING: %s partition could not be unmounted normally!\n", partitions[i].c_str());
|
|
umount2(mnt_point.c_str(), MNT_DETACH);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
LOGINFO("Finished.\n");
|
|
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
|
|
Set_Needed_Properties();
|
|
// Restart previously running services
|
|
for (size_t i = 0; i < Services.size(); ++i) {
|
|
if (Services[i].resume) {
|
|
if (Services[i].Service_Binary.find("keymaster") != string::npos) {
|
|
Wait_For_Property("hwservicemanager.ready", 500000, "true");
|
|
LOGINFO(" hwservicemanager is ready.\n");
|
|
}
|
|
|
|
Start_Service(Services[i].TWRP_Service_Name);
|
|
|
|
if (Services[i].Service_Binary == "qseecomd") {
|
|
if (Wait_For_Property("sys.listeners.registered", 500000, "true") == "true"
|
|
|| Wait_For_Property("vendor.sys.listeners.registered", 500000, "true") == "true")
|
|
LOGINFO(" qseecomd listeners registered.\n");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
|
|
Strace_init_Stop();
|
|
#endif
|
|
|
|
// Finish up and exit
|
|
if (fp_kmsg) {
|
|
fflush(fp_kmsg);
|
|
fclose(fp_kmsg);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
/*
|
|
* Common vold Response Codes / Errors:
|
|
* 406 (OpFailedStorageNotFound) -> Problem reading or parsing fstab
|
|
*
|
|
*/
|
|
|
|
/* Main function separated from core in case we want to return error info */
|
|
int vold_decrypt(const string& Password) {
|
|
return Vold_Decrypt_Core(Password);
|
|
}
|