From a363036b44f7f140aa9a943578f56abff5880a60 Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Tue, 17 May 2016 14:17:56 -0700 Subject: [PATCH] Two phases to set the password for disk encryption Revert "Revert "Two phases to set the password for disk encryption"" This reverts commit d402389290eeef86be7eb9241e20fdd125d44eb1. In addition, fix the bug in the original commit. Bug: 28154455 Bug: 28694324 Change-Id: I885f1d73e739416347c135d79979941c2bbdbe62 --- CryptCommandListener.cpp | 13 ++-- Ext4Crypt.cpp | 163 ++++++++++++++++++++++++++++++--------- Ext4Crypt.h | 5 +- 3 files changed, 138 insertions(+), 43 deletions(-) diff --git a/CryptCommandListener.cpp b/CryptCommandListener.cpp index f4ced43..02c2701 100644 --- a/CryptCommandListener.cpp +++ b/CryptCommandListener.cpp @@ -385,11 +385,14 @@ int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli, if (!check_argc(cli, subcommand, argc, 3, "")) return 0; return sendGenericOkFailOnBool(cli, e4crypt_destroy_user_key(atoi(argv[2]))); - } else if (subcommand == "change_user_key") { - if (!check_argc(cli, subcommand, argc, 7, - " ")) return 0; - return sendGenericOkFailOnBool(cli, e4crypt_change_user_key( - atoi(argv[2]), atoi(argv[3]), argv[4], argv[5], argv[6])); + } else if (subcommand == "add_user_key_auth") { + if (!check_argc(cli, subcommand, argc, 6, " ")) return 0; + return sendGenericOkFailOnBool(cli, e4crypt_add_user_key_auth( + atoi(argv[2]), atoi(argv[3]), argv[4], argv[5])); + + } else if (subcommand == "fixate_newest_user_key_auth") { + if (!check_argc(cli, subcommand, argc, 3, "")) return 0; + return sendGenericOkFailOnBool(cli, e4crypt_fixate_newest_user_key_auth(atoi(argv[2]))); } else if (subcommand == "unlock_user_key") { if (!check_argc(cli, subcommand, argc, 6, " ")) return 0; diff --git a/Ext4Crypt.cpp b/Ext4Crypt.cpp index 4e89575..1158475 100644 --- a/Ext4Crypt.cpp +++ b/Ext4Crypt.cpp @@ -19,6 +19,7 @@ #include "KeyStorage.h" #include "Utils.h" +#include #include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -175,16 +177,100 @@ 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_path(userid_t user_id) { - return StringPrintf("%s/ce/%d/current", user_key_dir.c_str(), user_id); +static std::string get_ce_key_directory_path(userid_t user_id) { + return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id); +} + +// Returns the keys newest first +static std::vector get_ce_key_paths(const std::string& directory_path) { + auto dirp = std::unique_ptr(opendir(directory_path.c_str()), closedir); + if (!dirp) { + PLOG(ERROR) << "Unable to open ce key directory: " + directory_path; + return std::vector(); + } + std::vector result; + for (;;) { + errno = 0; + auto const entry = readdir(dirp.get()); + if (!entry) { + if (errno) { + PLOG(ERROR) << "Unable to read ce key directory: " + directory_path; + return std::vector(); + } + break; + } + if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') { + LOG(DEBUG) << "Skipping non-key " << entry->d_name; + continue; + } + result.emplace_back(directory_path + "/" + entry->d_name); + } + std::sort(result.begin(), result.end()); + std::reverse(result.begin(), result.end()); + return result; +} + +static std::string get_ce_key_current_path(const std::string& directory_path) { + return directory_path + "/current"; +} + +static bool get_ce_key_new_path(const std::string& directory_path, + const std::vector& paths, + std::string *ce_key_path) { + if (paths.empty()) { + *ce_key_path = get_ce_key_current_path(directory_path); + return true; + } + for (unsigned int i = 0; i < UINT_MAX; i++) { + auto const candidate = StringPrintf("%s/cx%010u", directory_path.c_str(), i); + if (paths[0] < candidate) { + *ce_key_path = candidate; + return true; + } + } + return false; +} + +// Discard all keys but the named one; rename it to canonical name. +// No point in acting on errors in this; ignore them. +static void fixate_user_ce_key(const std::string& directory_path, const std::string &to_fix, + const std::vector& paths) { + for (auto const other_path: paths) { + if (other_path != to_fix) { + android::vold::destroyKey(other_path); + } + } + auto const current_path = get_ce_key_current_path(directory_path); + if (to_fix != current_path) { + LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path; + if (rename(to_fix.c_str(), current_path.c_str()) != 0) { + PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path; + } + } +} + +static bool read_and_fixate_user_ce_key(userid_t user_id, + const android::vold::KeyAuthentication& auth, + std::string *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; - const auto ce_key_path = get_ce_key_path(user_id); std::string ce_key; - if (!android::vold::retrieveKey(ce_key_path, auth, &ce_key)) return false; + if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false; std::string ce_raw_ref; if (!install_key(ce_key, &ce_raw_ref)) return false; s_ce_keys[user_id] = ce_key; @@ -252,12 +338,17 @@ static bool create_and_install_user_keys(userid_t user_id, bool 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 (!store_key(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 (!store_key(get_de_key_path(user_id), user_key_temp, kEmptyAuthentication, de_key)) return false; - if (!prepare_dir(user_key_dir + "/ce/" + std::to_string(user_id), - 0700, AID_ROOT, AID_ROOT)) return false; - if (!store_key(get_ce_key_path(user_id), user_key_temp, - kEmptyAuthentication, ce_key)) return false; } std::string de_raw_ref; if (!install_key(de_key, &de_raw_ref)) return false; @@ -374,15 +465,7 @@ bool e4crypt_init_user0() { 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; - auto de_path = get_de_key_path(0); - auto ce_path = get_ce_key_path(0); - if (!path_exists(de_path) || !path_exists(ce_path)) { - if (path_exists(de_path)) { - android::vold::destroyKey(de_path); // May be partially created so ignore errors - } - if (path_exists(ce_path)) { - android::vold::destroyKey(ce_path); // May be partially created so ignore errors - } + if (!path_exists(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 @@ -456,7 +539,9 @@ bool e4crypt_destroy_user_key(userid_t user_id) { if (it != s_ephemeral_users.end()) { s_ephemeral_users.erase(it); } else { - success &= android::vold::destroyKey(get_ce_key_path(user_id)); + for (auto const path: get_ce_key_paths(get_ce_key_directory_path(user_id))) { + success &= android::vold::destroyKey(path); + } success &= android::vold::destroyKey(get_de_key_path(user_id)); } return success; @@ -504,35 +589,41 @@ static bool parse_hex(const char* hex, std::string* result) { return true; } -bool e4crypt_change_user_key(userid_t user_id, int serial, const char* token_hex, - const char* old_secret_hex, const char* new_secret_hex) { - LOG(DEBUG) << "e4crypt_change_user_key " << user_id << " serial=" << serial +bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token_hex, + const char* secret_hex) { + LOG(DEBUG) << "e4crypt_add_user_key_auth " << user_id << " serial=" << serial << " token_present=" << (strcmp(token_hex, "!") != 0); if (!e4crypt_is_native()) return true; if (s_ephemeral_users.count(user_id) != 0) return true; - std::string token, old_secret, new_secret; + std::string token, secret; if (!parse_hex(token_hex, &token)) return false; - if (!parse_hex(old_secret_hex, &old_secret)) return false; - if (!parse_hex(new_secret_hex, &new_secret)) return false; - auto old_auth = old_secret.empty() ? kEmptyAuthentication - : android::vold::KeyAuthentication(token, old_secret); - auto new_auth = new_secret.empty() ? kEmptyAuthentication - : android::vold::KeyAuthentication(token, new_secret); + 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; } auto ce_key = it->second; - auto ce_key_path = get_ce_key_path(user_id); - std::string trial_key; - if (!android::vold::retrieveKey(ce_key_path, old_auth, &trial_key)) { - LOG(WARNING) << "change_user_key wasn't given enough info to reconstruct the key"; - } else if (ce_key != trial_key) { - LOG(WARNING) << "Reconstructed key != stored key"; + 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 (!store_key(ce_key_path, user_key_temp, auth, ce_key)) return false; + return true; +} + +bool e4crypt_fixate_newest_user_key_auth(userid_t user_id) { + LOG(DEBUG) << "e4crypt_fixate_newest_user_key_auth " << user_id; + if (!e4crypt_is_native()) return true; + 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; } - android::vold::destroyKey(ce_key_path); - if (!store_key(ce_key_path, user_key_temp, new_auth, ce_key)) return false; + fixate_user_ce_key(directory_path, paths[0], paths); return true; } diff --git a/Ext4Crypt.h b/Ext4Crypt.h index 89cfbab..2dcc197 100644 --- a/Ext4Crypt.h +++ b/Ext4Crypt.h @@ -28,8 +28,9 @@ bool e4crypt_initialize_global_de(); bool e4crypt_init_user0(); bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral); bool e4crypt_destroy_user_key(userid_t user_id); -bool e4crypt_change_user_key(userid_t user_id, int serial, const char* token, - const char* old_secret, const char* new_secret); +bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token, + const char* secret); +bool e4crypt_fixate_newest_user_key_auth(userid_t user_id); bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token, const char* secret); bool e4crypt_lock_user_key(userid_t user_id);