OSDN Git Service

Progress towards FBE and adoptable storage.
[android-x86/system-vold.git] / CryptCommandListener.cpp
index 173be63..779338f 100644 (file)
  * 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>
@@ -43,6 +50,8 @@
 #include "ResponseCode.h"
 #include "cryptfs.h"
 #include "Ext4Crypt.h"
+#include "MetadataCrypt.h"
+#include "Utils.h"
 
 #define DUMP_ARGS 0
 
@@ -83,8 +92,8 @@ void CryptCommandListener::dumpArgs(int argc, char **argv, int argObscure) {
 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);
@@ -110,6 +119,58 @@ static int getType(const char* type)
     }
 }
 
+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)) {
@@ -118,72 +179,99 @@ int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
     }
 
     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;
@@ -202,21 +290,15 @@ int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
         }
         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.
@@ -237,18 +319,23 @@ int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
             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()) {
@@ -269,7 +356,8 @@ int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
             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();
@@ -284,43 +372,69 @@ int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
             }
         }
         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;
     }