New granular encryption commands for framework.
We now have separate methods for key creation/destruction and
unlocking/locking. Key unlocking can pass through an opaque token,
but it's left empty for now.
Extend user storage setup to also create system_ce and user_de
paths. Bring over some path generation logic from installd.
Use strong type checking on user arguments.
Bug: 22358539
Change-Id: I00ba15c7b10dd682640b3f082feade4fb7cbbb5d
diff --git a/CryptCommandListener.cpp b/CryptCommandListener.cpp
index 575ec90..9feb5ec 100644
--- a/CryptCommandListener.cpp
+++ b/CryptCommandListener.cpp
@@ -16,6 +16,7 @@
#include <stdlib.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -30,7 +31,9 @@
#define LOG_TAG "VoldCryptCmdListener"
+#include <base/logging.h>
#include <base/stringprintf.h>
+
#include <cutils/fs.h>
#include <cutils/log.h>
#include <cutils/sockets.h>
@@ -43,6 +46,7 @@
#include "ResponseCode.h"
#include "cryptfs.h"
#include "Ext4Crypt.h"
+#include "Utils.h"
#define DUMP_ARGS 0
@@ -110,6 +114,14 @@
}
}
+static char* parseNull(char* arg) {
+ if (strcmp(arg, "!") == 0) {
+ return nullptr;
+ } else {
+ return arg;
+ }
+}
+
int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
@@ -124,6 +136,7 @@
int rc = 0;
+ std::string cmd(argv[1]);
if (!strcmp(argv[1], "checkpw")) {
if (argc != 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs checkpw <passwd>", false);
@@ -338,26 +351,7 @@
SLOGD("cryptfs setusercryptopolicies");
dumpArgs(argc, argv, -1);
rc = e4crypt_set_user_crypto_policies(argv[2]);
- } else if (!strcmp(argv[1], "createnewuserdir")) {
- if (argc != 4) {
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: cryptfs createnewuserdir <userHandle> <path>", false);
- return 0;
- }
- // ext4enc:TODO: send a CommandSyntaxError if argv[2] not an integer
- SLOGD("cryptfs createnewuserdir");
- dumpArgs(argc, argv, -1);
- rc = e4crypt_create_new_user_dir(argv[2], argv[3]);
- } else if (!strcmp(argv[1], "deleteuserkey")) {
- if (argc != 3) {
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: cryptfs deleteuserkey <userHandle>", false);
- return 0;
- }
- // ext4enc:TODO: send a CommandSyntaxError if argv[2] not an integer
- SLOGD("cryptfs deleteuserkey");
- dumpArgs(argc, argv, -1);
- rc = e4crypt_delete_user_key(argv[2]);
+
} else if (!strcmp(argv[1], "isConvertibleToFBE")) {
if (argc != 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
@@ -368,6 +362,28 @@
SLOGD("cryptfs isConvertibleToFBE");
dumpArgs(argc, argv, -1);
rc = cryptfs_isConvertibleToFBE();
+
+ } else if (cmd == "create_user_key" && argc > 3) {
+ // create_user_key [user] [serial]
+ return sendGenericOkFail(cli, e4crypt_create_user_key(atoi(argv[2])));
+
+ } else if (cmd == "destroy_user_key" && argc > 2) {
+ // destroy_user_key [user]
+ return sendGenericOkFail(cli, e4crypt_destroy_user_key(atoi(argv[2])));
+
+ } else if (cmd == "unlock_user_key" && argc > 4) {
+ // unlock_user_key [user] [serial] [token]
+ return sendGenericOkFail(cli, e4crypt_unlock_user_key(atoi(argv[2]), parseNull(argv[4])));
+
+ } else if (cmd == "lock_user_key" && argc > 2) {
+ // lock_user_key [user]
+ return sendGenericOkFail(cli, e4crypt_lock_user_key(atoi(argv[2])));
+
+ } else if (cmd == "prepare_user_storage" && argc > 4) {
+ // prepare_user_storage [uuid] [user] [serial]
+ return sendGenericOkFail(cli,
+ e4crypt_prepare_user_storage(parseNull(argv[2]), atoi(argv[3])));
+
} else {
dumpArgs(argc, argv, -1);
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false);
diff --git a/Ext4Crypt.cpp b/Ext4Crypt.cpp
index c337dbb..68aa240 100644
--- a/Ext4Crypt.cpp
+++ b/Ext4Crypt.cpp
@@ -1,5 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include "Ext4Crypt.h"
+#include "Utils.h"
+
#include <iomanip>
#include <map>
#include <fstream>
@@ -23,11 +41,17 @@
#include "ext4_crypt_init_extensions.h"
#define LOG_TAG "Ext4Crypt"
-#include "cutils/log.h"
+
+#include <cutils/fs.h>
+#include <cutils/log.h>
#include <cutils/klog.h>
+
#include <base/file.h>
+#include <base/logging.h>
#include <base/stringprintf.h>
+using android::base::StringPrintf;
+
namespace {
// Key length in bits
const int key_length = 128;
@@ -509,18 +533,14 @@
.Set(fieldname, std::string(value)) ? 0 : -1;
}
-static std::string get_key_path(
- const char *mount_path,
- const char *user_handle)
-{
+static std::string get_key_path(const char *mount_path, userid_t user_id) {
// ext4enc:TODO get the path properly
- auto key_dir = android::base::StringPrintf("%s/misc/vold/user_keys",
- mount_path);
- if (mkdir(key_dir.c_str(), 0700) < 0 && errno != EEXIST) {
- SLOGE("Unable to create %s (%s)", key_dir.c_str(), strerror(errno));
+ 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 key_dir + "/" + user_handle;
+ return StringPrintf("%s/%d", key_dir.c_str(), user_id);
}
// ext4enc:TODO this can't be the only place keys are read from /dev/urandom
@@ -562,13 +582,11 @@
return key;
}
-static int e4crypt_set_user_policy(const char *mount_path, const char *user_handle,
- const char *path, bool create_if_absent)
-{
- SLOGD("e4crypt_set_user_policy for %s", user_handle);
- auto user_key = e4crypt_get_key(
- get_key_path(mount_path, user_handle),
- create_if_absent);
+static int e4crypt_set_user_policy(const char *mount_path, userid_t user_id,
+ const char *path, bool create_if_absent) {
+ 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);
if (user_key.empty()) {
return -1;
}
@@ -579,24 +597,6 @@
return do_policy_set(path, raw_ref.c_str(), raw_ref.size());
}
-int e4crypt_create_new_user_dir(const char *user_handle, const char *path) {
- SLOGD("e4crypt_create_new_user_dir(\"%s\", \"%s\")", user_handle, path);
- if (mkdir(path, S_IRWXU | S_IRWXG | S_IXOTH) < 0) {
- return -1;
- }
- if (chmod(path, S_IRWXU | S_IRWXG | S_IXOTH) < 0) {
- return -1;
- }
- if (chown(path, AID_SYSTEM, AID_SYSTEM) < 0) {
- return -1;
- }
- if (e4crypt_crypto_complete(DATA_MNT_POINT) == 0) {
- // ext4enc:TODO handle errors from this.
- e4crypt_set_user_policy(DATA_MNT_POINT, user_handle, path, true);
- }
- return 0;
-}
-
static bool is_numeric(const char *name) {
for (const char *p = name; *p != '\0'; p++) {
if (!isdigit(*p))
@@ -626,10 +626,10 @@
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", result->d_name,
- user_dir.c_str(), false)) {
+ if (e4crypt_set_user_policy("/data", user_id, user_dir.c_str(), 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());
@@ -638,9 +638,20 @@
return 0;
}
-int e4crypt_delete_user_key(const char *user_handle) {
- SLOGD("e4crypt_delete_user_key(\"%s\")", user_handle);
- auto key_path = get_key_path(DATA_MNT_POINT, user_handle);
+int e4crypt_create_user_key(userid_t user_id) {
+ SLOGD("e4crypt_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).empty()) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+int e4crypt_destroy_user_key(userid_t user_id) {
+ SLOGD("e4crypt_destroy_user_key(%d)", 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);
auto ext4_key = fill_key(key);
auto ref = keyname(generate_key_ref(ext4_key.raw, ext4_key.size));
@@ -669,3 +680,66 @@
// ext4enc:TODO reap the zombie
return 0;
}
+
+int e4crypt_unlock_user_key(userid_t user_id, const char* token) {
+ if (property_get_bool("vold.emulate_fbe", false)) {
+ // When in emulation mode, we just use chmod
+ if (chmod(android::vold::BuildDataSystemCePath(user_id).c_str(), 0771) ||
+ chmod(android::vold::BuildDataUserPath(nullptr, user_id).c_str(), 0771)) {
+ PLOG(ERROR) << "Failed to unlock user " << user_id;
+ return -1;
+ }
+ } else {
+ auto user_key = e4crypt_get_key(get_key_path(DATA_MNT_POINT, user_id), false);
+ if (user_key.empty()) {
+ return -1;
+ }
+ auto raw_ref = e4crypt_install_key(user_key);
+ if (raw_ref.empty()) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int e4crypt_lock_user_key(userid_t user_id) {
+ if (property_get_bool("vold.emulate_fbe", false)) {
+ // When in emulation mode, we just use chmod
+ if (chmod(android::vold::BuildDataSystemCePath(user_id).c_str(), 0000) ||
+ chmod(android::vold::BuildDataUserPath(nullptr, user_id).c_str(), 0000)) {
+ PLOG(ERROR) << "Failed to lock user " << user_id;
+ return -1;
+ }
+ } else {
+ // TODO: remove from kernel keyring
+ }
+ return 0;
+}
+
+int e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id) {
+ std::string system_ce_path(android::vold::BuildDataSystemCePath(user_id));
+ std::string user_ce_path(android::vold::BuildDataUserPath(volume_uuid, user_id));
+ std::string user_de_path(android::vold::BuildDataUserDePath(volume_uuid, user_id));
+
+ if (fs_prepare_dir(system_ce_path.c_str(), 0700, AID_SYSTEM, AID_SYSTEM)) {
+ PLOG(ERROR) << "Failed to prepare " << system_ce_path;
+ return -1;
+ }
+ if (fs_prepare_dir(user_ce_path.c_str(), 0771, AID_SYSTEM, AID_SYSTEM)) {
+ PLOG(ERROR) << "Failed to prepare " << user_ce_path;
+ return -1;
+ }
+ if (fs_prepare_dir(user_de_path.c_str(), 0771, AID_SYSTEM, AID_SYSTEM)) {
+ PLOG(ERROR) << "Failed to prepare " << user_de_path;
+ return -1;
+ }
+
+ if (e4crypt_crypto_complete(DATA_MNT_POINT) == 0) {
+ if (e4crypt_set_user_policy(DATA_MNT_POINT, user_id, system_ce_path.c_str(), true)
+ || e4crypt_set_user_policy(DATA_MNT_POINT, user_id, user_ce_path.c_str(), true)) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/Ext4Crypt.h b/Ext4Crypt.h
index f5c2871..43b229c 100644
--- a/Ext4Crypt.h
+++ b/Ext4Crypt.h
@@ -1,6 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
#include <stddef.h>
#include <sys/cdefs.h>
+#include <cutils/multiuser.h>
+
__BEGIN_DECLS
// General functions
@@ -19,7 +37,13 @@
int e4crypt_set_field(const char* path, const char* fieldname,
const char* value);
int e4crypt_set_user_crypto_policies(const char *path);
-int e4crypt_create_new_user_dir(const char *user_handle, const char *path);
-int e4crypt_delete_user_key(const char *user_handle);
+
+int e4crypt_create_user_key(userid_t user_id);
+int e4crypt_destroy_user_key(userid_t user_id);
+
+int e4crypt_unlock_user_key(userid_t user_id, const char* token);
+int e4crypt_lock_user_key(userid_t user_id);
+
+int e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id);
__END_DECLS
diff --git a/Utils.cpp b/Utils.cpp
index d4618af..8f72b44 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -547,10 +547,55 @@
return res;
}
+static bool isValidFilename(const std::string& name) {
+ if (name.empty() || (name == ".") || (name == "..")
+ || (name.find('/') != std::string::npos)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
std::string BuildKeyPath(const std::string& partGuid) {
return StringPrintf("%s/expand_%s.key", kKeyPath, partGuid.c_str());
}
+std::string BuildDataSystemCePath(userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(nullptr));
+ return StringPrintf("%s/system_ce/%u", data.c_str(), userId);
+}
+
+std::string BuildDataPath(const char* volumeUuid) {
+ // TODO: unify with installd path generation logic
+ if (volumeUuid == nullptr) {
+ return "/data";
+ } else {
+ CHECK(isValidFilename(volumeUuid));
+ return StringPrintf("/mnt/expand/%s", volumeUuid);
+ }
+}
+
+std::string BuildDataUserPath(const char* volumeUuid, userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(volumeUuid));
+ if (volumeUuid == nullptr) {
+ if (userId == 0) {
+ return StringPrintf("%s/data", data.c_str());
+ } else {
+ return StringPrintf("%s/user/%u", data.c_str(), userId);
+ }
+ } else {
+ return StringPrintf("%s/user/%u", data.c_str(), userId);
+ }
+}
+
+std::string BuildDataUserDePath(const char* volumeUuid, userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(volumeUuid));
+ return StringPrintf("%s/user_de/%u", data.c_str(), userId);
+}
+
dev_t GetDevice(const std::string& path) {
struct stat sb;
if (stat(path.c_str(), &sb)) {
diff --git a/Utils.h b/Utils.h
index 228727a..c88325a 100644
--- a/Utils.h
+++ b/Utils.h
@@ -18,6 +18,7 @@
#define ANDROID_VOLD_UTILS_H
#include <utils/Errors.h>
+#include <cutils/multiuser.h>
#include <selinux/selinux.h>
#include <vector>
@@ -93,6 +94,12 @@
std::string BuildKeyPath(const std::string& partGuid);
+std::string BuildDataSystemCePath(userid_t userid);
+
+std::string BuildDataPath(const char* volumeUuid);
+std::string BuildDataUserPath(const char* volumeUuid, userid_t userid);
+std::string BuildDataUserDePath(const char* volumeUuid, userid_t userid);
+
dev_t GetDevice(const std::string& path);
std::string DefaultFstabPath();