Rework FBE crypto to match the N way of doing things
Major rework and refactor of FBE code to load the keys at the right time and in a natural way. The old code was aimed at our goals for M, with patches on top, and didn't quite work. Bug: 22358539 Change-Id: I9bf7a0a86ee3f2abf0edbd5966f93efac2474c2c
This commit is contained in:
parent
7a9dd95cbc
commit
285956fe11
4 changed files with 109 additions and 129 deletions
|
@ -346,11 +346,6 @@ int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
|
|||
dumpArgs(argc, argv, -1);
|
||||
cryptfs_clear_password();
|
||||
rc = 0;
|
||||
} else if (subcommand == "setusercryptopolicies") {
|
||||
if (!check_argc(cli, subcommand, argc, 3, "<path>")) return 0;
|
||||
SLOGD("cryptfs setusercryptopolicies");
|
||||
dumpArgs(argc, argv, -1);
|
||||
rc = e4crypt_vold_set_user_crypto_policies(argv[2]);
|
||||
|
||||
} else if (subcommand == "isConvertibleToFBE") {
|
||||
if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
|
||||
|
@ -372,7 +367,8 @@ int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
|
|||
|
||||
} else if (subcommand == "unlock_user_key") {
|
||||
if (!check_argc(cli, subcommand, argc, 5, "<user> <serial> <token>")) return 0;
|
||||
return sendGenericOkFail(cli, e4crypt_unlock_user_key(atoi(argv[2]), parseNull(argv[4])));
|
||||
return sendGenericOkFail(cli, e4crypt_unlock_user_key(
|
||||
atoi(argv[2]), atoi(argv[3]), parseNull(argv[4])));
|
||||
|
||||
} else if (subcommand == "lock_user_key") {
|
||||
if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
|
||||
|
|
223
Ext4Crypt.cpp
223
Ext4Crypt.cpp
|
@ -74,6 +74,8 @@ namespace {
|
|||
// How long do we store passwords for?
|
||||
const int password_max_age_seconds = 60;
|
||||
|
||||
const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys";
|
||||
|
||||
// How is device encrypted
|
||||
struct keys {
|
||||
std::string master_key;
|
||||
|
@ -83,9 +85,10 @@ namespace {
|
|||
std::map<std::string, keys> s_key_store;
|
||||
// Maps the key paths of ephemeral keys to the keys
|
||||
std::map<std::string, std::string> s_ephemeral_user_keys;
|
||||
// Map user serial numbers to key references
|
||||
std::map<int, std::string> s_key_raw_refs;
|
||||
|
||||
// ext4enc:TODO get these consts from somewhere good
|
||||
const int SHA512_LENGTH = 64;
|
||||
// ext4enc:TODO get this const from somewhere good
|
||||
const int EXT4_KEY_DESCRIPTOR_SIZE = 8;
|
||||
|
||||
// ext4enc:TODO Include structure from somewhere sensible
|
||||
|
@ -333,12 +336,12 @@ static std::string generate_key_ref(const char* key, int length)
|
|||
|
||||
SHA512_Init(&c);
|
||||
SHA512_Update(&c, key, length);
|
||||
unsigned char key_ref1[SHA512_LENGTH];
|
||||
unsigned char key_ref1[SHA512_DIGEST_LENGTH];
|
||||
SHA512_Final(key_ref1, &c);
|
||||
|
||||
SHA512_Init(&c);
|
||||
SHA512_Update(&c, key_ref1, SHA512_LENGTH);
|
||||
unsigned char key_ref2[SHA512_LENGTH];
|
||||
SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
|
||||
unsigned char key_ref2[SHA512_DIGEST_LENGTH];
|
||||
SHA512_Final(key_ref2, &c);
|
||||
|
||||
return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
|
@ -427,17 +430,15 @@ static key_serial_t e4crypt_keyring()
|
|||
static int e4crypt_install_key(const ext4_encryption_key &ext4_key, const std::string &ref)
|
||||
{
|
||||
key_serial_t device_keyring = e4crypt_keyring();
|
||||
SLOGI("Found device_keyring - id is %d", device_keyring);
|
||||
key_serial_t key_id = add_key("logon", ref.c_str(),
|
||||
(void*)&ext4_key, sizeof(ext4_key),
|
||||
device_keyring);
|
||||
if (key_id == -1) {
|
||||
SLOGE("Failed to insert key into keyring with error %s",
|
||||
strerror(errno));
|
||||
PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring;
|
||||
return -1;
|
||||
}
|
||||
SLOGI("Added key %d (%s) to keyring %d in process %d",
|
||||
key_id, ref.c_str(), device_keyring, getpid());
|
||||
LOG(INFO) << "Added key " << key_id << " (" << ref << ") to keyring "
|
||||
<< device_keyring << " in process " << getpid();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -548,148 +549,112 @@ int e4crypt_set_field(const char* path, const char* fieldname,
|
|||
.Set(fieldname, std::string(value)) ? 0 : -1;
|
||||
}
|
||||
|
||||
static std::string get_key_path(const char *mount_path, userid_t user_id) {
|
||||
// ext4enc:TODO get the path properly
|
||||
auto key_dir = StringPrintf("%s/misc/vold/user_keys", mount_path);
|
||||
if (fs_prepare_dir(key_dir.c_str(), 0700, AID_ROOT, AID_ROOT)) {
|
||||
PLOG(ERROR) << "Failed to prepare " << key_dir;
|
||||
return "";
|
||||
}
|
||||
return StringPrintf("%s/%d", key_dir.c_str(), user_id);
|
||||
static std::string get_key_path(userid_t user_id) {
|
||||
return StringPrintf("%s/%d", user_key_dir.c_str(), user_id);
|
||||
}
|
||||
|
||||
static bool e4crypt_is_key_ephemeral(const std::string &key_path) {
|
||||
return s_ephemeral_user_keys.find(key_path) != s_ephemeral_user_keys.end();
|
||||
}
|
||||
|
||||
// ext4enc:TODO this can't be the only place keys are read from /dev/urandom
|
||||
// we should unite those places.
|
||||
static std::string e4crypt_get_key(
|
||||
const std::string &key_path,
|
||||
bool create_if_absent,
|
||||
bool create_ephemeral)
|
||||
static std::string read_user_key(userid_t user_id)
|
||||
{
|
||||
const auto key_path = get_key_path(user_id);
|
||||
const auto ephemeral_key_it = s_ephemeral_user_keys.find(key_path);
|
||||
if (ephemeral_key_it != s_ephemeral_user_keys.end()) {
|
||||
return ephemeral_key_it->second;
|
||||
}
|
||||
|
||||
std::string content;
|
||||
if (android::base::ReadFileToString(key_path, &content)) {
|
||||
if (content.size() != key_length/8) {
|
||||
SLOGE("Wrong size key %zu in %s", content.size(), key_path.c_str());
|
||||
return "";
|
||||
}
|
||||
return content;
|
||||
}
|
||||
if (!create_if_absent) {
|
||||
SLOGE("No key found in %s", key_path.c_str());
|
||||
if (!android::base::ReadFileToString(key_path, &content)) {
|
||||
return "";
|
||||
}
|
||||
if (content.size() != key_length/8) {
|
||||
LOG(ERROR) << "Wrong size key " << content.size() << " in " << key_path;
|
||||
return "";
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
// ext4enc:TODO this can't be the only place keys are read from /dev/urandom
|
||||
// we should unite those places.
|
||||
static std::string get_random_string(size_t length) {
|
||||
std::ifstream urandom("/dev/urandom");
|
||||
if (!urandom) {
|
||||
SLOGE("Unable to open /dev/urandom (%s)", strerror(errno));
|
||||
PLOG(ERROR) << "Unable to open /dev/urandom";
|
||||
return "";
|
||||
}
|
||||
char key_bytes[key_length / 8];
|
||||
errno = 0;
|
||||
urandom.read(key_bytes, sizeof(key_bytes));
|
||||
std::string res(length, '\0');
|
||||
urandom.read(&res[0], length);
|
||||
if (!urandom) {
|
||||
SLOGE("Unable to read key from /dev/urandom (%s)", strerror(errno));
|
||||
PLOG(ERROR) << "Unable to read from /dev/urandom";
|
||||
return "";
|
||||
}
|
||||
std::string key(key_bytes, sizeof(key_bytes));
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool create_user_key(userid_t user_id, bool create_ephemeral) {
|
||||
if (fs_prepare_dir(user_key_dir.c_str(), 0700, AID_ROOT, AID_ROOT)) {
|
||||
PLOG(ERROR) << "Failed to prepare " << user_key_dir;
|
||||
return false;
|
||||
}
|
||||
const auto key_path = get_key_path(user_id);
|
||||
auto key = get_random_string(key_length / 8);
|
||||
if (key.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (create_ephemeral) {
|
||||
// If the key should be created as ephemeral, store it in memory only.
|
||||
s_ephemeral_user_keys[key_path] = key;
|
||||
} else if (!android::base::WriteStringToFile(key, key_path)) {
|
||||
SLOGE("Unable to write key to %s (%s)",
|
||||
key_path.c_str(), strerror(errno));
|
||||
return "";
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
static int e4crypt_set_user_policy(const char *mount_path, userid_t user_id,
|
||||
std::string& path, bool create_if_absent, bool create_ephemeral) {
|
||||
SLOGD("e4crypt_set_user_policy for %d", user_id);
|
||||
auto user_key = e4crypt_get_key(get_key_path(mount_path, user_id),
|
||||
create_if_absent, create_ephemeral);
|
||||
if (user_key.empty()) {
|
||||
return -1;
|
||||
}
|
||||
auto raw_ref = e4crypt_install_key(user_key);
|
||||
if (raw_ref.empty()) {
|
||||
return -1;
|
||||
}
|
||||
return do_policy_set(path.c_str(), raw_ref.c_str(), raw_ref.size());
|
||||
}
|
||||
|
||||
static bool is_numeric(const char *name) {
|
||||
for (const char *p = name; *p != '\0'; p++) {
|
||||
if (!isdigit(*p))
|
||||
return false;
|
||||
PLOG(ERROR) << "Unable to write key to " << key_path;
|
||||
return false;
|
||||
}
|
||||
LOG(DEBUG) << "Created key " << key_path;
|
||||
return true;
|
||||
}
|
||||
|
||||
int e4crypt_vold_set_user_crypto_policies(const char *dir)
|
||||
{
|
||||
if (e4crypt_crypto_complete(DATA_MNT_POINT) != 0) {
|
||||
return 0;
|
||||
}
|
||||
SLOGD("e4crypt_vold_set_user_crypto_policies");
|
||||
std::unique_ptr<DIR, int(*)(DIR*)> dirp(opendir(dir), closedir);
|
||||
if (!dirp) {
|
||||
SLOGE("Unable to read directory %s, error %s\n",
|
||||
dir, strerror(errno));
|
||||
static int e4crypt_set_user_policy(userid_t user_id, int serial, std::string& path) {
|
||||
LOG(DEBUG) << "e4crypt_set_user_policy for " << user_id << " serial " << serial;
|
||||
if (s_key_raw_refs.count(serial) != 1) {
|
||||
LOG(ERROR) << "Key unknown, can't e4crypt_set_user_policy for "
|
||||
<< user_id << " serial " << serial;
|
||||
return -1;
|
||||
}
|
||||
for (;;) {
|
||||
struct dirent *result = readdir(dirp.get());
|
||||
if (!result) {
|
||||
// ext4enc:TODO check errno
|
||||
break;
|
||||
}
|
||||
if (result->d_type != DT_DIR || !is_numeric(result->d_name)) {
|
||||
continue; // skips user 0, which is a symlink
|
||||
}
|
||||
auto user_id = atoi(result->d_name);
|
||||
auto user_dir = std::string() + dir + "/" + result->d_name;
|
||||
// ext4enc:TODO don't hardcode /data
|
||||
if (e4crypt_set_user_policy("/data", user_id, user_dir, false, false)) {
|
||||
// ext4enc:TODO If this function fails, stop the boot: we must
|
||||
// deliver on promised encryption.
|
||||
SLOGE("Unable to set policy on %s\n", user_dir.c_str());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
auto raw_ref = s_key_raw_refs[serial];
|
||||
return do_policy_set(path.c_str(), raw_ref.data(), raw_ref.size());
|
||||
}
|
||||
|
||||
int e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) {
|
||||
SLOGD("e4crypt_vold_create_user_key(%d)", user_id);
|
||||
// TODO: create second key for user_de data
|
||||
if (e4crypt_get_key(
|
||||
get_key_path(DATA_MNT_POINT, user_id), true, ephemeral).empty()) {
|
||||
return -1;
|
||||
} else {
|
||||
LOG(DEBUG) << "e4crypt_vold_create_user_key for " << user_id << " serial " << serial;
|
||||
if (!read_user_key(user_id).empty()) {
|
||||
LOG(ERROR) << "Already exists, can't e4crypt_vold_create_user_key for "
|
||||
<< user_id << " serial " << serial;
|
||||
// FIXME should we fail the command?
|
||||
return 0;
|
||||
}
|
||||
if (!create_user_key(user_id, ephemeral)) {
|
||||
return -1;
|
||||
}
|
||||
if (e4crypt_unlock_user_key(user_id, serial, nullptr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
// TODO: create second key for user_de data
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e4crypt_destroy_user_key(userid_t user_id) {
|
||||
SLOGD("e4crypt_destroy_user_key(%d)", user_id);
|
||||
LOG(DEBUG) << "e4crypt_destroy_user_key(" << user_id << ")";
|
||||
// TODO: destroy second key for user_de data
|
||||
auto key_path = get_key_path(DATA_MNT_POINT, user_id);
|
||||
auto key = e4crypt_get_key(key_path, false, false);
|
||||
auto key_path = get_key_path(user_id);
|
||||
auto key = read_user_key(user_id);
|
||||
auto ext4_key = fill_key(key);
|
||||
auto ref = keyname(generate_key_ref(ext4_key.raw, ext4_key.size));
|
||||
auto key_serial = keyctl_search(e4crypt_keyring(), "logon", ref.c_str(), 0);
|
||||
if (keyctl_revoke(key_serial) == 0) {
|
||||
SLOGD("Revoked key with serial %ld ref %s\n", key_serial, ref.c_str());
|
||||
LOG(DEBUG) << "Revoked key with serial " << key_serial << " ref " << ref;
|
||||
} else {
|
||||
SLOGE("Failed to revoke key with serial %ld ref %s: %s\n",
|
||||
key_serial, ref.c_str(), strerror(errno));
|
||||
PLOG(ERROR) << "Failed to revoke key with serial " << key_serial << " ref " << ref;
|
||||
}
|
||||
if (e4crypt_is_key_ephemeral(key_path)) {
|
||||
s_ephemeral_user_keys.erase(key_path);
|
||||
|
@ -697,18 +662,17 @@ int e4crypt_destroy_user_key(userid_t user_id) {
|
|||
}
|
||||
int pid = fork();
|
||||
if (pid < 0) {
|
||||
SLOGE("Unable to fork: %s", strerror(errno));
|
||||
PLOG(ERROR) << "Unable to fork";
|
||||
return -1;
|
||||
}
|
||||
if (pid == 0) {
|
||||
SLOGD("Forked for secdiscard");
|
||||
LOG(DEBUG) << "Forked for secdiscard";
|
||||
execl("/system/bin/secdiscard",
|
||||
"/system/bin/secdiscard",
|
||||
"--",
|
||||
key_path.c_str(),
|
||||
NULL);
|
||||
SLOGE("Unable to launch secdiscard on %s: %s\n", key_path.c_str(),
|
||||
strerror(errno));
|
||||
PLOG(ERROR) << "Unable to launch secdiscard on " << key_path;
|
||||
exit(-1);
|
||||
}
|
||||
// ext4enc:TODO reap the zombie
|
||||
|
@ -743,16 +707,37 @@ static int emulated_unlock(const std::string& path, mode_t mode) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int e4crypt_unlock_user_key(userid_t user_id, const char* token) {
|
||||
int e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token) {
|
||||
LOG(DEBUG) << "e4crypt_unlock_user_key " << user_id << " " << (token != nullptr);
|
||||
if (e4crypt_is_native()) {
|
||||
auto user_key = e4crypt_get_key(get_key_path(DATA_MNT_POINT, user_id), false, false);
|
||||
auto user_key = read_user_key(user_id);
|
||||
if (user_key.empty()) {
|
||||
return -1;
|
||||
// FIXME special case for user 0
|
||||
if (user_id != 0) {
|
||||
LOG(ERROR) << "Couldn't read key for " << user_id;
|
||||
return -1;
|
||||
}
|
||||
// FIXME if the key exists and we just failed to read it, this destroys it.
|
||||
if (!create_user_key(user_id, false)) {
|
||||
return -1;
|
||||
}
|
||||
user_key = read_user_key(user_id);
|
||||
if (user_key.empty()) {
|
||||
LOG(ERROR) << "Couldn't read just-created key for " << user_id;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
auto raw_ref = e4crypt_install_key(user_key);
|
||||
if (raw_ref.empty()) {
|
||||
return -1;
|
||||
}
|
||||
s_key_raw_refs[serial] = raw_ref;
|
||||
if (user_id == 0) {
|
||||
// FIXME special case for user 0
|
||||
// prepare their storage here
|
||||
e4crypt_prepare_user_storage(nullptr, 0, 0, false);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
// When in emulation mode, we just use chmod. However, we also
|
||||
// unlock directories when not in emulation mode, to bring devices
|
||||
|
@ -788,6 +773,11 @@ int e4crypt_prepare_user_storage(const char* volume_uuid,
|
|||
userid_t user_id,
|
||||
int serial,
|
||||
bool ephemeral) {
|
||||
if (volume_uuid) {
|
||||
LOG(DEBUG) << "e4crypt_prepare_user_storage " << volume_uuid << " " << user_id;
|
||||
} else {
|
||||
LOG(DEBUG) << "e4crypt_prepare_user_storage, null volume " << user_id;
|
||||
}
|
||||
std::string system_ce_path(android::vold::BuildDataSystemCePath(user_id));
|
||||
std::string media_ce_path(android::vold::BuildDataMediaPath(volume_uuid, user_id));
|
||||
std::string user_ce_path(android::vold::BuildDataUserPath(volume_uuid, user_id));
|
||||
|
@ -811,10 +801,9 @@ int e4crypt_prepare_user_storage(const char* volume_uuid,
|
|||
}
|
||||
|
||||
if (e4crypt_crypto_complete(DATA_MNT_POINT) == 0) {
|
||||
if (e4crypt_set_user_policy(DATA_MNT_POINT, user_id, system_ce_path, true, ephemeral)
|
||||
|| e4crypt_set_user_policy(DATA_MNT_POINT, user_id, media_ce_path, true, ephemeral)
|
||||
|| e4crypt_set_user_policy(DATA_MNT_POINT, user_id, user_ce_path, true,
|
||||
ephemeral)) {
|
||||
if (e4crypt_set_user_policy(user_id, serial, system_ce_path)
|
||||
|| e4crypt_set_user_policy(user_id, serial, media_ce_path)
|
||||
|| e4crypt_set_user_policy(user_id, serial, user_ce_path)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ __BEGIN_DECLS
|
|||
|
||||
// General functions
|
||||
int e4crypt_enable(const char* path);
|
||||
int e4crypt_main(int argc, char* argv[]);
|
||||
int e4crypt_change_password(const char* path, int crypt_type,
|
||||
const char* password);
|
||||
int e4crypt_crypto_complete(const char* path);
|
||||
|
@ -36,12 +35,11 @@ int e4crypt_get_field(const char* path, const char* fieldname,
|
|||
char* value, size_t len);
|
||||
int e4crypt_set_field(const char* path, const char* fieldname,
|
||||
const char* value);
|
||||
int e4crypt_vold_set_user_crypto_policies(const char *path);
|
||||
|
||||
int e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
|
||||
int e4crypt_destroy_user_key(userid_t user_id);
|
||||
|
||||
int e4crypt_unlock_user_key(userid_t user_id, const char* token);
|
||||
int e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token);
|
||||
int e4crypt_lock_user_key(userid_t user_id);
|
||||
|
||||
int e4crypt_prepare_user_storage(const char* volume_uuid,
|
||||
|
|
3
main.cpp
3
main.cpp
|
@ -97,9 +97,6 @@ int main(int argc, char** argv) {
|
|||
vm->setDebug(true);
|
||||
}
|
||||
|
||||
// Prepare owner storage
|
||||
e4crypt_prepare_user_storage(nullptr, 0, 0, false);
|
||||
|
||||
cl = new CommandListener();
|
||||
ccl = new CryptCommandListener();
|
||||
vm->setBroadcaster((SocketListener *) cl);
|
||||
|
|
Loading…
Reference in a new issue