Two phases to set the password for disk encryption
In one phase, we make the new password work, and in the second we make it the only one which works ("fixation"). This means that we can set the password in Gatekeeper between these two phases, and a crash doesn't break things. Unlocking a user automatically fixates the presented credential. Bug: 28154455 Change-Id: I54623c8652f0c9f72dd60388a7dc0ab2d48e81c7
This commit is contained in:
parent
b3de337acd
commit
92c5eeb467
3 changed files with 137 additions and 43 deletions
|
@ -385,11 +385,14 @@ int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
|
||||||
if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
|
if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
|
||||||
return sendGenericOkFailOnBool(cli, e4crypt_destroy_user_key(atoi(argv[2])));
|
return sendGenericOkFailOnBool(cli, e4crypt_destroy_user_key(atoi(argv[2])));
|
||||||
|
|
||||||
} else if (subcommand == "change_user_key") {
|
} else if (subcommand == "add_user_key_auth") {
|
||||||
if (!check_argc(cli, subcommand, argc, 7,
|
if (!check_argc(cli, subcommand, argc, 6, "<user> <serial> <token> <secret>")) return 0;
|
||||||
"<user> <serial> <token> <old_secret> <new_secret>")) return 0;
|
return sendGenericOkFailOnBool(cli, e4crypt_add_user_key_auth(
|
||||||
return sendGenericOkFailOnBool(cli, e4crypt_change_user_key(
|
atoi(argv[2]), atoi(argv[3]), argv[4], argv[5]));
|
||||||
atoi(argv[2]), atoi(argv[3]), argv[4], argv[5], argv[6]));
|
|
||||||
|
} else if (subcommand == "fixate_newest_user_key_auth") {
|
||||||
|
if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
|
||||||
|
return sendGenericOkFailOnBool(cli, e4crypt_fixate_newest_user_key_auth(atoi(argv[2])));
|
||||||
|
|
||||||
} else if (subcommand == "unlock_user_key") {
|
} else if (subcommand == "unlock_user_key") {
|
||||||
if (!check_argc(cli, subcommand, argc, 6, "<user> <serial> <token> <secret>")) return 0;
|
if (!check_argc(cli, subcommand, argc, 6, "<user> <serial> <token> <secret>")) return 0;
|
||||||
|
|
162
Ext4Crypt.cpp
162
Ext4Crypt.cpp
|
@ -19,6 +19,7 @@
|
||||||
#include "KeyStorage.h"
|
#include "KeyStorage.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
#include <selinux/android.h>
|
#include <selinux/android.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -183,16 +185,100 @@ static std::string get_de_key_path(userid_t user_id) {
|
||||||
return StringPrintf("%s/de/%d", user_key_dir.c_str(), 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) {
|
static std::string get_ce_key_directory_path(userid_t user_id) {
|
||||||
return StringPrintf("%s/ce/%d/current", user_key_dir.c_str(), 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
static bool read_and_install_user_ce_key(userid_t user_id,
|
||||||
const android::vold::KeyAuthentication& auth) {
|
const android::vold::KeyAuthentication& auth) {
|
||||||
if (s_ce_key_raw_refs.count(user_id) != 0) return true;
|
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;
|
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;
|
std::string ce_raw_ref;
|
||||||
if (!install_key(ce_key, &ce_raw_ref)) return false;
|
if (!install_key(ce_key, &ce_raw_ref)) return false;
|
||||||
s_ce_keys[user_id] = ce_key;
|
s_ce_keys[user_id] = ce_key;
|
||||||
|
@ -260,12 +346,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.
|
// If the key should be created as ephemeral, don't store it.
|
||||||
s_ephemeral_users.insert(user_id);
|
s_ephemeral_users.insert(user_id);
|
||||||
} else {
|
} 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,
|
if (!store_key(get_de_key_path(user_id), user_key_temp,
|
||||||
kEmptyAuthentication, de_key)) return false;
|
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;
|
std::string de_raw_ref;
|
||||||
if (!install_key(de_key, &de_raw_ref)) return false;
|
if (!install_key(de_key, &de_raw_ref)) return false;
|
||||||
|
@ -382,15 +473,7 @@ bool e4crypt_init_user0() {
|
||||||
if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
|
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 + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
|
||||||
if (!prepare_dir(user_key_dir + "/de", 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);
|
if (!path_exists(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 (!create_and_install_user_keys(0, false)) return false;
|
if (!create_and_install_user_keys(0, false)) return false;
|
||||||
}
|
}
|
||||||
// TODO: switch to loading only DE_0 here once framework makes
|
// TODO: switch to loading only DE_0 here once framework makes
|
||||||
|
@ -464,7 +547,9 @@ bool e4crypt_destroy_user_key(userid_t user_id) {
|
||||||
if (it != s_ephemeral_users.end()) {
|
if (it != s_ephemeral_users.end()) {
|
||||||
s_ephemeral_users.erase(it);
|
s_ephemeral_users.erase(it);
|
||||||
} else {
|
} 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));
|
success &= android::vold::destroyKey(get_de_key_path(user_id));
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
|
@ -512,35 +597,40 @@ static bool parse_hex(const char* hex, std::string* result) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool e4crypt_change_user_key(userid_t user_id, int serial, const char* token_hex,
|
bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token_hex,
|
||||||
const char* old_secret_hex, const char* new_secret_hex) {
|
const char* secret_hex) {
|
||||||
LOG(DEBUG) << "e4crypt_change_user_key " << user_id << " serial=" << serial
|
LOG(DEBUG) << "e4crypt_add_user_key_auth " << user_id << " serial=" << serial
|
||||||
<< " token_present=" << (strcmp(token_hex, "!") != 0);
|
<< " token_present=" << (strcmp(token_hex, "!") != 0);
|
||||||
if (!e4crypt_is_native()) return true;
|
if (!e4crypt_is_native()) return true;
|
||||||
if (s_ephemeral_users.count(user_id) != 0) 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(token_hex, &token)) return false;
|
||||||
if (!parse_hex(old_secret_hex, &old_secret)) return false;
|
if (!parse_hex(secret_hex, &secret)) return false;
|
||||||
if (!parse_hex(new_secret_hex, &new_secret)) return false;
|
auto auth = secret.empty() ? kEmptyAuthentication
|
||||||
auto old_auth = old_secret.empty() ? kEmptyAuthentication
|
: android::vold::KeyAuthentication(token, secret);
|
||||||
: android::vold::KeyAuthentication(token, old_secret);
|
|
||||||
auto new_auth = new_secret.empty() ? kEmptyAuthentication
|
|
||||||
: android::vold::KeyAuthentication(token, new_secret);
|
|
||||||
auto it = s_ce_keys.find(user_id);
|
auto it = s_ce_keys.find(user_id);
|
||||||
if (it == s_ce_keys.end()) {
|
if (it == s_ce_keys.end()) {
|
||||||
LOG(ERROR) << "Key not loaded into memory, can't change for user " << user_id;
|
LOG(ERROR) << "Key not loaded into memory, can't change for user " << user_id;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto ce_key = it->second;
|
auto ce_key = it->second;
|
||||||
auto ce_key_path = get_ce_key_path(user_id);
|
auto const directory_path = get_ce_key_directory_path(user_id);
|
||||||
std::string trial_key;
|
auto const paths = get_ce_key_paths(directory_path);
|
||||||
if (!android::vold::retrieveKey(ce_key_path, old_auth, &trial_key)) {
|
std::string ce_key_path;
|
||||||
LOG(WARNING) << "change_user_key wasn't given enough info to reconstruct the key";
|
if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
|
||||||
} else if (ce_key != trial_key) {
|
if (!store_key(ce_key_path, user_key_temp, auth, ce_key)) return false;
|
||||||
LOG(WARNING) << "Reconstructed key != stored key";
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool e4crypt_fixate_newest_user_key_auth(userid_t user_id) {
|
||||||
|
LOG(DEBUG) << "e4crypt_fixate_newest_user_key_auth " << user_id;
|
||||||
|
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);
|
fixate_user_ce_key(directory_path, paths[0], paths);
|
||||||
if (!store_key(ce_key_path, user_key_temp, new_auth, ce_key)) return false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,9 @@ bool e4crypt_initialize_global_de();
|
||||||
bool e4crypt_init_user0();
|
bool e4crypt_init_user0();
|
||||||
bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
|
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_destroy_user_key(userid_t user_id);
|
||||||
bool e4crypt_change_user_key(userid_t user_id, int serial, const char* token,
|
bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token,
|
||||||
const char* old_secret, const char* new_secret);
|
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_unlock_user_key(userid_t user_id, int serial, const char* token, const char* secret);
|
||||||
bool e4crypt_lock_user_key(userid_t user_id);
|
bool e4crypt_lock_user_key(userid_t user_id);
|
||||||
|
|
Loading…
Reference in a new issue