2 * Copyright (C) 2015 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <sys/socket.h>
21 #include <sys/types.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
36 #define LOG_TAG "VoldCryptCmdListener"
38 #include <android-base/logging.h>
39 #include <android-base/stringprintf.h>
41 #include <cutils/fs.h>
42 #include <cutils/log.h>
43 #include <cutils/sockets.h>
45 #include <sysutils/SocketClient.h>
46 #include <private/android_filesystem_config.h>
48 #include "CryptCommandListener.h"
50 #include "ResponseCode.h"
52 #include "Ext4Crypt.h"
53 #include "MetadataCrypt.h"
58 CryptCommandListener::CryptCommandListener() :
59 FrameworkListener("cryptd", true) {
60 registerCmd(new CryptfsCmd());
64 void CryptCommandListener::dumpArgs(int argc, char **argv, int argObscure) {
68 memset(buffer, 0, sizeof(buffer));
70 for (i = 0; i < argc; i++) {
71 unsigned int len = strlen(argv[i]) + 1; // Account for space
72 if (i == argObscure) {
73 len += 2; // Account for {}
75 if (((p - buffer) + len) < (sizeof(buffer)-1)) {
76 if (i == argObscure) {
92 void CryptCommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
95 int CryptCommandListener::sendGenericOkFailOnBool(SocketClient *cli, bool success) {
97 return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
99 return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
103 CryptCommandListener::CryptfsCmd::CryptfsCmd() :
104 VoldCommand("cryptfs") {
107 static int getType(const char* type)
109 if (!strcmp(type, "default")) {
110 return CRYPT_TYPE_DEFAULT;
111 } else if (!strcmp(type, "password")) {
112 return CRYPT_TYPE_PASSWORD;
113 } else if (!strcmp(type, "pin")) {
114 return CRYPT_TYPE_PIN;
115 } else if (!strcmp(type, "pattern")) {
116 return CRYPT_TYPE_PATTERN;
122 static char* parseNull(char* arg) {
123 if (strcmp(arg, "!") == 0) {
130 static bool check_argc(SocketClient *cli, const std::string &subcommand, int argc,
131 int expected, std::string usage) {
132 assert(expected >= 2);
134 assert(usage.empty());
136 assert(!usage.empty());
137 assert(std::count(usage.begin(), usage.end(), ' ') + 3 == expected);
139 if (argc == expected) {
142 auto message = std::string() + "Usage: cryptfs " + subcommand;
143 if (!usage.empty()) {
144 message += " " + usage;
146 cli->sendMsg(ResponseCode::CommandSyntaxError, message.c_str(), false);
150 static int do_enablecrypto(char* arg2, char* arg4, int type, bool no_ui) {
153 for (tries = 0; tries < 2; ++tries) {
154 if (type == CRYPT_TYPE_DEFAULT) {
155 rc = cryptfs_enable_default(arg2, no_ui);
157 rc = cryptfs_enable(arg2, type, arg4, no_ui);
164 } else if (tries == 0) {
165 Process::killProcessesWithOpenFiles(DATA_MNT_POINT, SIGKILL);
174 int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
175 int argc, char **argv) {
176 if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
177 cli->sendMsg(ResponseCode::CommandNoPermission, "No permission to run cryptfs commands", false);
182 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing subcommand", false);
188 std::string subcommand(argv[1]);
189 if (subcommand == "checkpw") {
190 if (!check_argc(cli, subcommand, argc, 3, "<passwd>")) return 0;
191 dumpArgs(argc, argv, 2);
192 rc = cryptfs_check_passwd(argv[2]);
193 } else if (subcommand == "restart") {
194 if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
195 dumpArgs(argc, argv, -1);
197 // Spawn as thread so init can issue commands back to vold without
198 // causing deadlock, usually as a result of prep_data_fs.
199 std::thread(&cryptfs_restart).detach();
200 } else if (subcommand == "cryptocomplete") {
201 if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
202 dumpArgs(argc, argv, -1);
203 rc = cryptfs_crypto_complete();
204 } else if (subcommand == "enablecrypto") {
205 if (e4crypt_is_native()) {
206 if (argc != 5 || strcmp(argv[2], "inplace") || strcmp(argv[3], "default")
207 || strcmp(argv[4], "noui")) {
208 cli->sendMsg(ResponseCode::CommandSyntaxError,
209 "Usage with ext4crypt: cryptfs enablecrypto inplace default noui", false);
212 return sendGenericOkFailOnBool(cli, e4crypt_enable_crypto());
214 const char* syntax = "Usage: cryptfs enablecrypto <wipe|inplace> "
215 "default|password|pin|pattern [passwd] [noui]";
217 // This should be replaced with a command line parser if more options
221 int type = CRYPT_TYPE_DEFAULT;
222 int options = 4; // Optional parameters are at this offset
224 // Minimum 4 parameters
226 } else if (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace") ) {
227 // Second parameter must be wipe or inplace
230 // Third parameter must be valid type
231 type = getType(argv[3]);
234 } else if (type != CRYPT_TYPE_DEFAULT) {
241 // Too few parameters
243 } else if (argc == options) {
245 } else if (argc == options + 1) {
246 // One option, must be noui
247 if (!strcmp(argv[options], "noui")) {
259 cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
263 dumpArgs(argc, argv, 4);
265 // Spawn as thread so init can issue commands back to vold without
266 // causing deadlock, usually as a result of prep_data_fs.
267 char* arg2 = argc > 2 ? strdup(argv[2]) : NULL;
268 char* arg4 = argc > 4 ? strdup(argv[4]) : NULL;
269 std::thread(&do_enablecrypto, arg2, arg4, type, no_ui).detach();
270 } else if (subcommand == "enablefilecrypto") {
271 if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
272 dumpArgs(argc, argv, -1);
273 rc = e4crypt_initialize_global_de();
274 } else if (subcommand == "changepw") {
275 const char* syntax = "Usage: cryptfs changepw "
276 "default|password|pin|pattern [newpasswd]";
277 const char* password;
280 } else if (argc == 4) {
283 cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
286 int type = getType(argv[2]);
288 cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
291 SLOGD("cryptfs changepw %s {}", argv[2]);
292 rc = cryptfs_changepw(type, password);
293 } else if (subcommand == "verifypw") {
294 if (!check_argc(cli, subcommand, argc, 3, "<passwd>")) return 0;
295 SLOGD("cryptfs verifypw {}");
296 rc = cryptfs_verify_passwd(argv[2]);
297 } else if (subcommand == "getfield") {
298 if (!check_argc(cli, subcommand, argc, 3, "<fieldname>")) return 0;
300 int valbuf_len = PROPERTY_VALUE_MAX;
302 dumpArgs(argc, argv, -1);
304 // Increase the buffer size until it is big enough for the field value stored.
306 valbuf = (char*)malloc(valbuf_len);
307 if (valbuf == NULL) {
308 cli->sendMsg(ResponseCode::OperationFailed, "Failed to allocate memory", false);
311 rc = cryptfs_getfield(argv[2], valbuf, valbuf_len);
312 if (rc != CRYPTO_GETFIELD_ERROR_BUF_TOO_SMALL) {
318 if (rc == CRYPTO_GETFIELD_OK) {
319 cli->sendMsg(ResponseCode::CryptfsGetfieldResult, valbuf, false);
322 } else if (subcommand == "setfield") {
323 if (!check_argc(cli, subcommand, argc, 4, "<fieldname> <value>")) return 0;
324 dumpArgs(argc, argv, -1);
325 rc = cryptfs_setfield(argv[2], argv[3]);
326 } else if (subcommand == "mountdefaultencrypted") {
327 if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
328 SLOGD("cryptfs mountdefaultencrypted");
329 dumpArgs(argc, argv, -1);
331 if (e4crypt_is_native()) {
332 return sendGenericOkFailOnBool(cli, e4crypt_mount_metadata_encrypted());
334 // Spawn as thread so init can issue commands back to vold without
335 // causing deadlock, usually as a result of prep_data_fs.
336 std::thread(&cryptfs_mount_default_encrypted).detach();
337 } else if (subcommand == "getpwtype") {
338 if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
339 SLOGD("cryptfs getpwtype");
340 dumpArgs(argc, argv, -1);
341 switch(cryptfs_get_password_type()) {
342 case CRYPT_TYPE_PASSWORD:
343 cli->sendMsg(ResponseCode::PasswordTypeResult, "password", false);
345 case CRYPT_TYPE_PATTERN:
346 cli->sendMsg(ResponseCode::PasswordTypeResult, "pattern", false);
349 cli->sendMsg(ResponseCode::PasswordTypeResult, "pin", false);
351 case CRYPT_TYPE_DEFAULT:
352 cli->sendMsg(ResponseCode::PasswordTypeResult, "default", false);
355 /** @TODO better error and make sure handled by callers */
356 cli->sendMsg(ResponseCode::OpFailedStorageNotFound, "Error", false);
359 } else if (subcommand == "getpw") {
360 if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
361 SLOGD("cryptfs getpw");
362 dumpArgs(argc, argv, -1);
363 const char* password = cryptfs_get_password();
366 int size = asprintf(&message, "{{sensitive}} %s", password);
368 cli->sendMsg(ResponseCode::CommandOkay, message, false);
369 memset(message, 0, size);
375 } else if (subcommand == "clearpw") {
376 if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
377 SLOGD("cryptfs clearpw");
378 dumpArgs(argc, argv, -1);
379 cryptfs_clear_password();
382 } else if (subcommand == "isConvertibleToFBE") {
383 if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
384 // ext4enc:TODO: send a CommandSyntaxError if argv[2] not an integer
385 SLOGD("cryptfs isConvertibleToFBE");
386 dumpArgs(argc, argv, -1);
387 rc = cryptfs_isConvertibleToFBE();
389 } else if (subcommand == "init_user0") {
390 if (!check_argc(cli, subcommand, argc, 2, "")) return 0;
391 return sendGenericOkFailOnBool(cli, e4crypt_init_user0());
393 } else if (subcommand == "create_user_key") {
394 if (!check_argc(cli, subcommand, argc, 5, "<user> <serial> <ephemeral>")) return 0;
395 return sendGenericOkFailOnBool(cli, e4crypt_vold_create_user_key(
396 atoi(argv[2]), atoi(argv[3]), atoi(argv[4]) != 0));
398 } else if (subcommand == "destroy_user_key") {
399 if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
400 return sendGenericOkFailOnBool(cli, e4crypt_destroy_user_key(atoi(argv[2])));
402 } else if (subcommand == "add_user_key_auth") {
403 if (!check_argc(cli, subcommand, argc, 6, "<user> <serial> <token> <secret>")) return 0;
404 return sendGenericOkFailOnBool(cli, e4crypt_add_user_key_auth(
405 atoi(argv[2]), atoi(argv[3]), argv[4], argv[5]));
407 } else if (subcommand == "fixate_newest_user_key_auth") {
408 if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
409 return sendGenericOkFailOnBool(cli, e4crypt_fixate_newest_user_key_auth(atoi(argv[2])));
411 } else if (subcommand == "unlock_user_key") {
412 if (!check_argc(cli, subcommand, argc, 6, "<user> <serial> <token> <secret>")) return 0;
413 return sendGenericOkFailOnBool(cli, e4crypt_unlock_user_key(
414 atoi(argv[2]), atoi(argv[3]), argv[4], argv[5]));
416 } else if (subcommand == "lock_user_key") {
417 if (!check_argc(cli, subcommand, argc, 3, "<user>")) return 0;
418 return sendGenericOkFailOnBool(cli, e4crypt_lock_user_key(atoi(argv[2])));
420 } else if (subcommand == "prepare_user_storage") {
421 if (!check_argc(cli, subcommand, argc, 6, "<uuid> <user> <serial> <flags>")) return 0;
422 return sendGenericOkFailOnBool(cli, e4crypt_prepare_user_storage(
423 parseNull(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5])));
425 } else if (subcommand == "destroy_user_storage") {
426 if (!check_argc(cli, subcommand, argc, 5, "<uuid> <user> <flags>")) return 0;
427 return sendGenericOkFailOnBool(cli,
428 e4crypt_destroy_user_storage(parseNull(argv[2]), atoi(argv[3]), atoi(argv[4])));
430 } else if (subcommand == "secdiscard") {
431 if (!check_argc(cli, subcommand, argc, 3, "<path>")) return 0;
432 return sendGenericOkFailOnBool(cli,
433 e4crypt_secdiscard(parseNull(argv[2])));
436 dumpArgs(argc, argv, -1);
437 cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs subcommand", false);
441 // Always report that the command succeeded and return the error code.
442 // The caller will check the return value to see what the error was.
444 snprintf(msg, sizeof(msg), "%d", rc);
445 cli->sendMsg(ResponseCode::CommandOkay, msg, false);