|
|
|
/*
|
|
|
|
* 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 <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 <dirent.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <fs_mgr.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <thread>
|
|
|
|
|
|
|
|
#define LOG_TAG "VoldCryptCmdListener"
|
|
|
|
|
|
|
|
#include <android-base/logging.h>
|
|
|
|
#include <android-base/stringprintf.h>
|
|
|
|
|
|
|
|
#include <cutils/fs.h>
|
|
|
|
#include <cutils/log.h>
|
|
|
|
#include <cutils/sockets.h>
|
|
|
|
|
|
|
|
#include <sysutils/SocketClient.h>
|
|
|
|
#include <private/android_filesystem_config.h>
|
|
|
|
|
|
|
|
#include "CryptCommandListener.h"
|
|
|
|
#include "Process.h"
|
|
|
|
#include "ResponseCode.h"
|
|
|
|
#include "cryptfs.h"
|
|
|
|
#include "Ext4Crypt.h"
|
|
|
|
#include "MetadataCrypt.h"
|
|
|
|
#include "Utils.h"
|
|
|
|
|
|
|
|
#define DUMP_ARGS 0
|
|
|
|
|
|
|
|
CryptCommandListener::CryptCommandListener() :
|
|
|
|
FrameworkListener("cryptd", true) {
|
|
|
|
registerCmd(new CryptfsCmd());
|
|
|
|
}
|
|
|
|
|
|
|
|
#if DUMP_ARGS
|
|
|
|
void CryptCommandListener::dumpArgs(int argc, char **argv, int argObscure) {
|
|
|
|
char buffer[4096];
|
|
|
|
char *p = buffer;
|
|
|
|
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
unsigned int len = strlen(argv[i]) + 1; // Account for space
|
|
|
|
if (i == argObscure) {
|
|
|
|
len += 2; // Account for {}
|
|
|
|
}
|
|
|
|
if (((p - buffer) + len) < (sizeof(buffer)-1)) {
|
|
|
|
if (i == argObscure) {
|
|
|
|
*p++ = '{';
|
|
|
|
*p++ = '}';
|
|
|
|
*p++ = ' ';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
strcpy(p, argv[i]);
|
|
|
|
p+= strlen(argv[i]);
|
|
|
|
if (i != (argc -1)) {
|
|
|
|
*p++ = ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SLOGD("%s", buffer);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
void CryptCommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
|
|
|
|
#endif
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CryptCommandListener::CryptfsCmd::CryptfsCmd() :
|
|
|
|
VoldCommand("cryptfs") {
|
|
|
|
}
|
|
|
|
|
|
|
|
static int getType(const char* type)
|
|
|
|
{
|
|
|
|
if (!strcmp(type, "default")) {
|
|
|
|
return CRYPT_TYPE_DEFAULT;
|
|
|
|
} else if (!strcmp(type, "password")) {
|
|
|
|
return CRYPT_TYPE_PASSWORD;
|
|
|
|
} else if (!strcmp(type, "pin")) {
|
|
|
|
return CRYPT_TYPE_PIN;
|
|
|
|
} else if (!strcmp(type, "pattern")) {
|
|
|
|
return CRYPT_TYPE_PATTERN;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run cryptfs commands", false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc < 2) {
|
|
|
|
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing subcommand", false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rc = 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 (subcommand == "restart") {
|
|
|
|
if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
|
|
|
|
dumpArgs(argc, argv, -1);
|
|
|
|
|
|
|
|
// 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 (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());
|
|
|
|
}
|
|
|
|
const char* syntax = "Usage: cryptfs enablecrypto <wipe|inplace> "
|
|
|
|
"default|password|pin|pattern [passwd] [noui]";
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
valid = false;
|
|
|
|
} else if (type != CRYPT_TYPE_DEFAULT) {
|
|
|
|
options++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = e4crypt_initialize_global_de();
|
|
|
|
} else if (subcommand == "changepw") {
|
|
|
|
const char* syntax = "Usage: cryptfs changepw "
|
|
|
|
"default|password|pin|pattern [newpasswd]";
|
|
|
|
const char* password;
|
|
|
|
if (argc == 3) {
|
|
|
|
password = "";
|
|
|
|
} else if (argc == 4) {
|
|
|
|
password = argv[3];
|
|
|
|
} else {
|
|
|
|
cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int type = getType(argv[2]);
|
|
|
|
if (type == -1) {
|
|
|
|
cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
SLOGD("cryptfs changepw %s {}", argv[2]);
|
|
|
|
rc = cryptfs_changepw(type, password);
|
|
|
|
} 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 (subcommand == "getfield") {
|
|
|
|
if (!check_argc(cli, subcommand, argc, 3, "<fieldname>")) return 0;
|
|
|
|
char *valbuf;
|
|
|
|
int valbuf_len = PROPERTY_VALUE_MAX;
|
|
|
|
|
|
|
|
dumpArgs(argc, argv, -1);
|
|
|
|
|
|
|
|
// Increase the buffer size until it is big enough for the field value stored.
|
|
|
|
while (1) {
|
|
|
|
valbuf = (char*)malloc(valbuf_len);
|
|
|
|
if (valbuf == NULL) {
|
|
|
|
cli->sendMsg(ResponseCode::OperationFailed, "Failed to allocate memory", false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
rc = cryptfs_getfield(argv[2], valbuf, valbuf_len);
|
|
|
|
if (rc != CRYPTO_GETFIELD_ERROR_BUF_TOO_SMALL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
free(valbuf);
|
|
|
|
valbuf_len *= 2;
|
|
|
|
}
|
|
|
|
if (rc == CRYPTO_GETFIELD_OK) {
|
|
|
|
cli->sendMsg(ResponseCode::CryptfsGetfieldResult, valbuf, false);
|
|
|
|
}
|
|
|
|
free(valbuf);
|
|
|
|
} 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 (subcommand == "mountdefaultencrypted") {
|
|
|
|
if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
|
|
|
|
SLOGD("cryptfs mountdefaultencrypted");
|
|
|
|
dumpArgs(argc, argv, -1);
|
|
|
|
|
|
|
|
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()) {
|
|
|
|
case CRYPT_TYPE_PASSWORD:
|
|
|
|
cli->sendMsg(ResponseCode::PasswordTypeResult, "password", false);
|
|
|
|
return 0;
|
|
|
|
case CRYPT_TYPE_PATTERN:
|
|
|
|
cli->sendMsg(ResponseCode::PasswordTypeResult, "pattern", false);
|
|
|
|
return 0;
|
|
|
|
case CRYPT_TYPE_PIN:
|
|
|
|
cli->sendMsg(ResponseCode::PasswordTypeResult, "pin", false);
|
|
|
|
return 0;
|
|
|
|
case CRYPT_TYPE_DEFAULT:
|
|
|
|
cli->sendMsg(ResponseCode::PasswordTypeResult, "default", false);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
/** @TODO better error and make sure handled by callers */
|
|
|
|
cli->sendMsg(ResponseCode::OpFailedStorageNotFound, "Error", false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} 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();
|
|
|
|
if (password) {
|
|
|
|
char* message = 0;
|
|
|
|
int size = asprintf(&message, "{{sensitive}} %s", password);
|
|
|
|
if (size != -1) {
|
|
|
|
cli->sendMsg(ResponseCode::CommandOkay, message, false);
|
|
|
|
memset(message, 0, size);
|
|
|
|
free (message);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rc = -1;
|
|
|
|
} 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 (subcommand == "isConvertibleToFBE") {
|
|
|
|
if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
|
|
|
|
// ext4enc:TODO: send a CommandSyntaxError if argv[2] not an integer
|
|
|
|
SLOGD("cryptfs isConvertibleToFBE");
|
|
|
|
dumpArgs(argc, argv, -1);
|
|
|
|
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 subcommand", false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Always report that the command succeeded and return the error code.
|
|
|
|
// The caller will check the return value to see what the error was.
|
|
|
|
char msg[255];
|
|
|
|
snprintf(msg, sizeof(msg), "%d", rc);
|
|
|
|
cli->sendMsg(ResponseCode::CommandOkay, msg, false);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|