* limitations under the License.
*/
+#include <assert.h>
#include <stdlib.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <inttypes.h>
+#include <algorithm>
+#include <thread>
+
#define LOG_TAG "VoldCryptCmdListener"
-#include <base/stringprintf.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
#include <cutils/fs.h>
#include <cutils/log.h>
#include <cutils/sockets.h>
#include "ResponseCode.h"
#include "cryptfs.h"
#include "Ext4Crypt.h"
+#include "MetadataCrypt.h"
+#include "Utils.h"
#define DUMP_ARGS 0
void CryptCommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
#endif
-int CryptCommandListener::sendGenericOkFail(SocketClient *cli, int cond) {
- if (!cond) {
+int CryptCommandListener::sendGenericOkFailOnBool(SocketClient *cli, bool success) {
+ if (success) {
return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
} else {
return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
}
}
+static char* parseNull(char* arg) {
+ if (strcmp(arg, "!") == 0) {
+ return nullptr;
+ } else {
+ return arg;
+ }
+}
+
+static bool check_argc(SocketClient *cli, const std::string &subcommand, int argc,
+ int expected, std::string usage) {
+ assert(expected >= 2);
+ if (expected == 2) {
+ assert(usage.empty());
+ } else {
+ assert(!usage.empty());
+ assert(std::count(usage.begin(), usage.end(), ' ') + 3 == expected);
+ }
+ if (argc == expected) {
+ return true;
+ }
+ auto message = std::string() + "Usage: cryptfs " + subcommand;
+ if (!usage.empty()) {
+ message += " " + usage;
+ }
+ cli->sendMsg(ResponseCode::CommandSyntaxError, message.c_str(), false);
+ return false;
+}
+
+static int do_enablecrypto(char* arg2, char* arg4, int type, bool no_ui) {
+ int rc;
+ int tries;
+ for (tries = 0; tries < 2; ++tries) {
+ if (type == CRYPT_TYPE_DEFAULT) {
+ rc = cryptfs_enable_default(arg2, no_ui);
+ } else {
+ rc = cryptfs_enable(arg2, type, arg4, no_ui);
+ }
+
+ if (rc == 0) {
+ free(arg2);
+ free(arg4);
+ return 0;
+ } else if (tries == 0) {
+ Process::killProcessesWithOpenFiles(DATA_MNT_POINT, SIGKILL);
+ }
+ }
+
+ free(arg2);
+ free(arg4);
+ return -1;
+}
+
int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
}
if (argc < 2) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing subcommand", false);
return 0;
}
int rc = 0;
- if (!strcmp(argv[1], "checkpw")) {
- if (argc != 3) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs checkpw <passwd>", false);
- return 0;
- }
+ std::string subcommand(argv[1]);
+ if (subcommand == "checkpw") {
+ if (!check_argc(cli, subcommand, argc, 3, "<passwd>")) return 0;
dumpArgs(argc, argv, 2);
rc = cryptfs_check_passwd(argv[2]);
- } else if (!strcmp(argv[1], "restart")) {
- if (argc != 2) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs restart", false);
- return 0;
- }
+ } else if (subcommand == "restart") {
+ if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
dumpArgs(argc, argv, -1);
- rc = cryptfs_restart();
- } else if (!strcmp(argv[1], "cryptocomplete")) {
- if (argc != 2) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs cryptocomplete", false);
- return 0;
- }
+
+ // Spawn as thread so init can issue commands back to vold without
+ // causing deadlock, usually as a result of prep_data_fs.
+ std::thread(&cryptfs_restart).detach();
+ } else if (subcommand == "cryptocomplete") {
+ if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
dumpArgs(argc, argv, -1);
rc = cryptfs_crypto_complete();
- } else if (!strcmp(argv[1], "enablecrypto")) {
- const char* syntax = "Usage: cryptfs enablecrypto <wipe|inplace> "
- "default|password|pin|pattern [passwd]";
- if ( (argc != 4 && argc != 5)
- || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
- return 0;
+ } else if (subcommand == "enablecrypto") {
+ if (e4crypt_is_native()) {
+ if (argc != 5 || strcmp(argv[2], "inplace") || strcmp(argv[3], "default")
+ || strcmp(argv[4], "noui")) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage with ext4crypt: cryptfs enablecrypto inplace default noui", false);
+ return 0;
+ }
+ return sendGenericOkFailOnBool(cli, e4crypt_enable_crypto());
}
- dumpArgs(argc, argv, 4);
+ const char* syntax = "Usage: cryptfs enablecrypto <wipe|inplace> "
+ "default|password|pin|pattern [passwd] [noui]";
- int tries;
- for (tries = 0; tries < 2; ++tries) {
- int type = getType(argv[3]);
+ // This should be replaced with a command line parser if more options
+ // are added
+ bool valid = true;
+ bool no_ui = false;
+ int type = CRYPT_TYPE_DEFAULT;
+ int options = 4; // Optional parameters are at this offset
+ if (argc < 4) {
+ // Minimum 4 parameters
+ valid = false;
+ } else if (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace") ) {
+ // Second parameter must be wipe or inplace
+ valid = false;
+ } else {
+ // Third parameter must be valid type
+ type = getType(argv[3]);
if (type == -1) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, syntax,
- false);
- return 0;
- } else if (type == CRYPT_TYPE_DEFAULT) {
- rc = cryptfs_enable_default(argv[2], /*allow_reboot*/false);
- } else {
- rc = cryptfs_enable(argv[2], type, argv[4],
- /*allow_reboot*/false);
+ valid = false;
+ } else if (type != CRYPT_TYPE_DEFAULT) {
+ options++;
}
+ }
- if (rc == 0) {
- break;
- } else if (tries == 0) {
- Process::killProcessesWithOpenFiles(DATA_MNT_POINT, SIGKILL);
+ if (valid) {
+ if(argc < options) {
+ // Too few parameters
+ valid = false;
+ } else if (argc == options) {
+ // No more, done
+ } else if (argc == options + 1) {
+ // One option, must be noui
+ if (!strcmp(argv[options], "noui")) {
+ no_ui = true;
+ } else {
+ valid = false;
+ }
+ } else {
+ // Too many options
+ valid = false;
}
}
- } else if (!strcmp(argv[1], "enablefilecrypto")) {
- const char* syntax = "Usage: cryptfs enablefilecrypto";
- if (argc != 2) {
+
+ if (!valid) {
cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
return 0;
}
+
+ dumpArgs(argc, argv, 4);
+
+ // Spawn as thread so init can issue commands back to vold without
+ // causing deadlock, usually as a result of prep_data_fs.
+ char* arg2 = argc > 2 ? strdup(argv[2]) : NULL;
+ char* arg4 = argc > 4 ? strdup(argv[4]) : NULL;
+ std::thread(&do_enablecrypto, arg2, arg4, type, no_ui).detach();
+ } else if (subcommand == "enablefilecrypto") {
+ if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
dumpArgs(argc, argv, -1);
- rc = cryptfs_enable_file();
- } else if (!strcmp(argv[1], "changepw")) {
+ rc = e4crypt_initialize_global_de();
+ } else if (subcommand == "changepw") {
const char* syntax = "Usage: cryptfs changepw "
"default|password|pin|pattern [newpasswd]";
const char* password;
}
SLOGD("cryptfs changepw %s {}", argv[2]);
rc = cryptfs_changepw(type, password);
- } else if (!strcmp(argv[1], "verifypw")) {
- if (argc != 3) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs verifypw <passwd>", false);
- return 0;
- }
+ } else if (subcommand == "verifypw") {
+ if (!check_argc(cli, subcommand, argc, 3, "<passwd>")) return 0;
SLOGD("cryptfs verifypw {}");
rc = cryptfs_verify_passwd(argv[2]);
- } else if (!strcmp(argv[1], "getfield")) {
+ } else if (subcommand == "getfield") {
+ if (!check_argc(cli, subcommand, argc, 3, "<fieldname>")) return 0;
char *valbuf;
int valbuf_len = PROPERTY_VALUE_MAX;
- if (argc != 3) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs getfield <fieldname>", false);
- return 0;
- }
dumpArgs(argc, argv, -1);
// Increase the buffer size until it is big enough for the field value stored.
cli->sendMsg(ResponseCode::CryptfsGetfieldResult, valbuf, false);
}
free(valbuf);
- } else if (!strcmp(argv[1], "setfield")) {
- if (argc != 4) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs setfield <fieldname> <value>", false);
- return 0;
- }
+ } else if (subcommand == "setfield") {
+ if (!check_argc(cli, subcommand, argc, 4, "<fieldname> <value>")) return 0;
dumpArgs(argc, argv, -1);
rc = cryptfs_setfield(argv[2], argv[3]);
- } else if (!strcmp(argv[1], "mountdefaultencrypted")) {
+ } else if (subcommand == "mountdefaultencrypted") {
+ if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
SLOGD("cryptfs mountdefaultencrypted");
dumpArgs(argc, argv, -1);
- rc = cryptfs_mount_default_encrypted();
- } else if (!strcmp(argv[1], "getpwtype")) {
+
+ if (e4crypt_is_native()) {
+ return sendGenericOkFailOnBool(cli, e4crypt_mount_metadata_encrypted());
+ }
+ // Spawn as thread so init can issue commands back to vold without
+ // causing deadlock, usually as a result of prep_data_fs.
+ std::thread(&cryptfs_mount_default_encrypted).detach();
+ } else if (subcommand == "getpwtype") {
+ if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
SLOGD("cryptfs getpwtype");
dumpArgs(argc, argv, -1);
switch(cryptfs_get_password_type()) {
cli->sendMsg(ResponseCode::OpFailedStorageNotFound, "Error", false);
return 0;
}
- } else if (!strcmp(argv[1], "getpw")) {
+ } else if (subcommand == "getpw") {
+ if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
SLOGD("cryptfs getpw");
dumpArgs(argc, argv, -1);
const char* password = cryptfs_get_password();
}
}
rc = -1;
- } else if (!strcmp(argv[1], "clearpw")) {
+ } else if (subcommand == "clearpw") {
+ if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
SLOGD("cryptfs clearpw");
dumpArgs(argc, argv, -1);
cryptfs_clear_password();
rc = 0;
- } else if (!strcmp(argv[1], "setusercryptopolicies")) {
- if (argc != 3) {
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: cryptfs setusercryptopolicies <path>", false);
- return 0;
- }
- 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;
- }
+
+ } else if (subcommand == "isConvertibleToFBE") {
+ if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
// ext4enc:TODO: send a CommandSyntaxError if argv[2] not an integer
- SLOGD("cryptfs deleteuserkey");
+ SLOGD("cryptfs isConvertibleToFBE");
dumpArgs(argc, argv, -1);
- rc = e4crypt_delete_user_key(argv[2]);
+ rc = cryptfs_isConvertibleToFBE();
+
+ } else if (subcommand == "init_user0") {
+ if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
+ return sendGenericOkFailOnBool(cli, e4crypt_init_user0());
+
+ } else if (subcommand == "create_user_key") {
+ if (!check_argc(cli, subcommand, argc, 5, "<user> <serial> <ephemeral>")) return 0;
+ return sendGenericOkFailOnBool(cli, e4crypt_vold_create_user_key(
+ atoi(argv[2]), atoi(argv[3]), atoi(argv[4]) != 0));
+
+ } else if (subcommand == "destroy_user_key") {
+ if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
+ return sendGenericOkFailOnBool(cli, e4crypt_destroy_user_key(atoi(argv[2])));
+
+ } else if (subcommand == "add_user_key_auth") {
+ if (!check_argc(cli, subcommand, argc, 6, "<user> <serial> <token> <secret>")) 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, "<user>")) 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, "<user> <serial> <token> <secret>")) return 0;
+ return sendGenericOkFailOnBool(cli, e4crypt_unlock_user_key(
+ atoi(argv[2]), atoi(argv[3]), argv[4], argv[5]));
+
+ } else if (subcommand == "lock_user_key") {
+ if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
+ return sendGenericOkFailOnBool(cli, e4crypt_lock_user_key(atoi(argv[2])));
+
+ } else if (subcommand == "prepare_user_storage") {
+ if (!check_argc(cli, subcommand, argc, 6, "<uuid> <user> <serial> <flags>")) return 0;
+ return sendGenericOkFailOnBool(cli, e4crypt_prepare_user_storage(
+ parseNull(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5])));
+
+ } else if (subcommand == "destroy_user_storage") {
+ if (!check_argc(cli, subcommand, argc, 5, "<uuid> <user> <flags>")) return 0;
+ return sendGenericOkFailOnBool(cli,
+ e4crypt_destroy_user_storage(parseNull(argv[2]), atoi(argv[3]), atoi(argv[4])));
+
+ } else if (subcommand == "secdiscard") {
+ if (!check_argc(cli, subcommand, argc, 3, "<path>")) return 0;
+ return sendGenericOkFailOnBool(cli,
+ e4crypt_secdiscard(parseNull(argv[2])));
+
} else {
dumpArgs(argc, argv, -1);
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false);
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs subcommand", false);
return 0;
}