Add support for multi-user decryption
* Add CLI support using the command "twrp decrypt <PASSWORD> [USER ID]"
* Add GUI support using the menu Advanced --> Decrypt User
multiuser: Parse users only when Decrypt_DE runs successfully
multiuser: Warn when not all users are decrypted
Change-Id: Ia5e943b13c2d5ec5c34ae97661133c19ff471e6d
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
index 79de55f..5c279ab 100755
--- a/partitionmanager.cpp
+++ b/partitionmanager.cpp
@@ -115,7 +115,8 @@
using android::fs_mgr::FstabEntry;
extern bool datamedia;
-
+std::vector<users_struct> Users_List;
+
TWPartitionManager::TWPartitionManager(void) {
mtp_was_enabled = false;
mtp_write_fd = -1;
@@ -447,10 +448,12 @@
}
} else {
DataManager::SetValue("TW_CRYPTO_TYPE", password_type);
+ DataManager::SetValue("tw_crypto_pwtype_0", password_type);
}
}
}
- if (Decrypt_Data && (!Decrypt_Data->Is_Encrypted || Decrypt_Data->Is_Decrypted) && Decrypt_Data->Mount(false)) {
+ if (Decrypt_Data && (!Decrypt_Data->Is_Encrypted || Decrypt_Data->Is_Decrypted) &&
+ Decrypt_Data->Mount(false)) {
Decrypt_Adopted();
}
#endif
@@ -1734,11 +1737,11 @@
} else {
gui_msg("decrypt_success_nodev=Data successfully decrypted");
}
+ property_set("twrp.decrypt.done", "true");
dat->Setup_File_System(false);
- dat->Current_File_System = dat->Fstab_File_System; // Needed if we're ignoring blkid because encrypted devices start out as emmc
+ dat->Current_File_System = dat->Fstab_File_System; // Needed if we're ignoring blkid because encrypted devices start out as emmc
- // Sleep for a bit so that the device will be ready
- sleep(1);
+ sleep(1); // Sleep for a bit so that the device will be ready
if (dat->Has_Data_Media && dat->Mount(false) && TWFunc::Path_Exists("/data/media/0")) {
dat->Storage_Path = "/data/media/0";
dat->Symlink_Path = dat->Storage_Path;
@@ -1753,7 +1756,94 @@
LOGERR("Unable to locate data partition.\n");
}
-int TWPartitionManager::Decrypt_Device(string Password) {
+void TWPartitionManager::Parse_Users() {
+#ifdef TW_INCLUDE_FBE
+ char user_check_result[PROPERTY_VALUE_MAX];
+ for (int userId = 0; userId <= 9999; userId++) {
+ string prop = "twrp.user." + to_string(userId) + ".decrypt";
+ property_get(prop.c_str(), user_check_result, "-1");
+ if (strcmp(user_check_result, "-1") != 0) {
+ if (userId < 0 || userId > 9999) {
+ LOGINFO("Incorrect user id %d\n", userId);
+ continue;
+ }
+ struct users_struct user;
+ user.userId = to_string(userId);
+
+ // Attempt to get name of user. Fallback to user ID if this fails.
+ char* userFile = PageManager::LoadFileToBuffer("/data/system/users/" + to_string(userId) + ".xml", NULL);
+ if (userFile == NULL)
+ user.userName = to_string(userId);
+ else {
+ xml_document<> *userXml = new xml_document<>();
+ userXml->parse<0>(userFile);
+ xml_node<>* userNode = userXml->first_node("user");
+ if (userNode == nullptr) {
+ user.userName = to_string(userId);
+ } else {
+ xml_node<>* nameNode = userNode->first_node("name");
+ if (nameNode == nullptr)
+ user.userName = to_string(userId);
+ else {
+ string userName = nameNode->value();
+ user.userName = userName + " (" + to_string(userId) + ")";
+ }
+ }
+ }
+
+ string filename;
+ user.type = Get_Password_Type(userId, filename);
+
+ user.isDecrypted = false;
+ if (strcmp(user_check_result, "1") == 0)
+ user.isDecrypted = true;
+ Users_List.push_back(user);
+ }
+ }
+ Check_Users_Decryption_Status();
+#endif
+}
+
+std::vector<users_struct>* TWPartitionManager::Get_Users_List() {
+ return &Users_List;
+}
+
+void TWPartitionManager::Mark_User_Decrypted(int userID) {
+#ifdef TW_INCLUDE_FBE
+ std::vector<users_struct>::iterator iter;
+ for (iter = Users_List.begin(); iter != Users_List.end(); iter++) {
+ if (atoi((*iter).userId.c_str()) == userID) {
+ (*iter).isDecrypted = true;
+ string user_prop_decrypted = "twrp.user." + to_string(userID) + ".decrypt";
+ property_set(user_prop_decrypted.c_str(), "1");
+ break;
+ }
+ }
+ Check_Users_Decryption_Status();
+#endif
+}
+
+void TWPartitionManager::Check_Users_Decryption_Status() {
+#ifdef TW_INCLUDE_FBE
+ int all_is_decrypted = 1;
+ std::vector<users_struct>::iterator iter;
+ for (iter = Users_List.begin(); iter != Users_List.end(); iter++) {
+ if (!(*iter).isDecrypted) {
+ LOGINFO("User %s is not decrypted.\n", (*iter).userId.c_str());
+ all_is_decrypted = 0;
+ break;
+ }
+ }
+ if (all_is_decrypted == 1) {
+ LOGINFO("All found users are decrypted.\n");
+ DataManager::SetValue("tw_all_users_decrypted", "1");
+ property_set("twrp.all.users.decrypted", "true");
+ } else
+ DataManager::SetValue("tw_all_users_decrypted", "0");
+#endif
+}
+
+int TWPartitionManager::Decrypt_Device(string Password, int user_id) {
#ifdef TW_INCLUDE_CRYPTO
char crypto_state[PROPERTY_VALUE_MAX], crypto_blkdev[PROPERTY_VALUE_MAX];
std::vector<TWPartition*>::iterator iter;
@@ -1777,14 +1867,49 @@
#ifdef TW_INCLUDE_FBE
if (!Mount_By_Path("/data", true)) // /data has to be mounted for FBE
return -1;
+
+ bool user_need_decrypt = false;
+ std::vector<users_struct>::iterator iter;
+ for (iter = Users_List.begin(); iter != Users_List.end(); iter++) {
+ if (atoi((*iter).userId.c_str()) == user_id && !(*iter).isDecrypted) {
+ user_need_decrypt = true;
+ }
+ }
+ if (!user_need_decrypt) {
+ LOGINFO("User %d does not require decryption\n", user_id);
+ return 0;
+ }
+
int retry_count = 10;
while (!TWFunc::Path_Exists("/data/system/users/gatekeeper.password.key") && --retry_count)
- usleep(2000); // A small sleep is needed after mounting /data to ensure reliable decrypt... maybe because of DE?
- int user_id = DataManager::GetIntValue("tw_decrypt_user_id");
- LOGINFO("Decrypting FBE for user %i\n", user_id);
+ usleep(2000); // A small sleep is needed after mounting /data to ensure reliable decrypt...maybe because of DE?
+ gui_msg(Msg("decrypting_user_fbe=Attempting to decrypt FBE for user {1}...")(user_id));
if (Decrypt_User(user_id, Password)) {
- Post_Decrypt("");
+ gui_msg(Msg("decrypt_user_success_fbe=User {1} Decrypted Successfully")(user_id));
+ Mark_User_Decrypted(user_id);
+ if (user_id == 0) {
+ // When decrypting user 0 also try all other users
+ std::vector<users_struct>::iterator iter;
+ for (iter = Users_List.begin(); iter != Users_List.end(); iter++) {
+ if ((*iter).userId == "0" || (*iter).isDecrypted)
+ continue;
+
+ int tmp_user_id = atoi((*iter).userId.c_str());
+ gui_msg(Msg("decrypting_user_fbe=Attempting to decrypt FBE for user {1}...")(tmp_user_id));
+ if (Decrypt_User(tmp_user_id, Password) ||
+ (Password != "!" && Decrypt_User(tmp_user_id, "!"))) { // "!" means default password
+ gui_msg(Msg("decrypt_user_success_fbe=User {1} Decrypted Successfully")(tmp_user_id));
+ Mark_User_Decrypted(tmp_user_id);
+ } else {
+ gui_msg(Msg("decrypt_user_fail_fbe=Failed to decrypt user {1}")(tmp_user_id));
+ }
+ }
+ Post_Decrypt("");
+ }
+
return 0;
+ } else {
+ gui_msg(Msg(msg::kError, "decrypt_user_fail_fbe=Failed to decrypt user {1}")(user_id));
}
#else
LOGERR("FBE support is not present\n");
@@ -1792,6 +1917,13 @@
return -1;
}
+ char isdecrypteddata[PROPERTY_VALUE_MAX];
+ property_get("twrp.decrypt.done", isdecrypteddata, "");
+ if (strcmp(isdecrypteddata, "true") == 0) {
+ LOGINFO("Data has no decryption required\n");
+ return 0;
+ }
+
int pwret = -1;
pid_t pid = fork();
if (pid < 0) {