Add the new verifypw command to vold/cryptfs

This vold command returns 0 if the given password matches the password
used to decrypt the device on boot.  It returns 1 if they don't match,
and it returns -1 on an internal error, and -2 if the device is not encrypted.

Also check the uid of the sender of the command and only allow the root and
system users to issue cryptfs commands.

Change-Id: I5e5ae3b72a2d7814ae68c2d49aa9deb90fb1dac5
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 3a83d2a..97ed2ce 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -28,6 +28,7 @@
 #include <cutils/log.h>
 
 #include <sysutils/SocketClient.h>
+#include <private/android_filesystem_config.h>
 
 #include "CommandListener.h"
 #include "VolumeManager.h"
@@ -498,6 +499,11 @@
 
 int CommandListener::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 Argument", false);
         return 0;
@@ -540,6 +546,13 @@
         } 
         SLOGD("cryptfs changepw {}");
         rc = cryptfs_changepw(argv[2]);
+    } else if (!strcmp(argv[1], "verifypw")) {
+        if (argc != 3) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs verifypw <passwd>", false);
+            return 0;
+        }
+        SLOGD("cryptfs verifypw {}");
+        rc = cryptfs_verify_passwd(argv[2]);
     } else {
         dumpArgs(argc, argv, -1);
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false);
diff --git a/ResponseCode.h b/ResponseCode.h
index a858b99..402e35b 100644
--- a/ResponseCode.h
+++ b/ResponseCode.h
@@ -48,6 +48,7 @@
     // action did not take place.
     static const int CommandSyntaxError = 500;
     static const int CommandParameterError = 501;
+    static const int CommandNoPermission = 502;
 
     // 600 series - Unsolicited broadcasts
     static const int UnsolicitedInformational       = 600;
diff --git a/cryptfs.c b/cryptfs.c
index e3672f7..052c033 100644
--- a/cryptfs.c
+++ b/cryptfs.c
@@ -62,6 +62,7 @@
 
 static unsigned char saved_master_key[KEY_LEN_BYTES];
 static char *saved_data_blkdev;
+static char *saved_mount_point;
 static int  master_key_saved = 0;
 
 static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags)
@@ -841,6 +842,7 @@
      */
     memcpy(saved_master_key, decrypted_master_key, KEY_LEN_BYTES);
     saved_data_blkdev = strdup(real_blkdev);
+    saved_mount_point = strdup(mount_point);
     master_key_saved = 1;
     rc = 0;
   }
@@ -914,6 +916,63 @@
     return rc;
 }
 
+int cryptfs_verify_passwd(char *passwd)
+{
+    struct crypt_mnt_ftr crypt_ftr;
+    /* Allocate enough space for a 256 bit key, but we may use less */
+    unsigned char encrypted_master_key[32], decrypted_master_key[32];
+    unsigned char salt[SALT_LEN];
+    char real_blkdev[MAXPATHLEN];
+    char fs_type[PROPERTY_VALUE_MAX];
+    char fs_options[PROPERTY_VALUE_MAX];
+    unsigned long mnt_flags;
+    char encrypted_state[PROPERTY_VALUE_MAX];
+    int rc;
+
+    property_get("ro.crypto.state", encrypted_state, "");
+    if (strcmp(encrypted_state, "encrypted") ) {
+        SLOGE("device not encrypted, aborting");
+        return -2;
+    }
+
+    if (!master_key_saved) {
+        SLOGE("encrypted fs not yet mounted, aborting");
+        return -1;
+    }
+
+    if (!saved_mount_point) {
+        SLOGE("encrypted fs failed to save mount point, aborting");
+        return -1;
+    }
+
+    if (get_orig_mount_parms(saved_mount_point, fs_type, real_blkdev, &mnt_flags, fs_options)) {
+        SLOGE("Error reading original mount parms for mount point %s\n", saved_mount_point);
+        return -1;
+    }
+
+    if (get_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key, salt)) {
+        SLOGE("Error getting crypt footer and key\n");
+        return -1;
+    }
+
+    if (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) {
+        /* If the device has no password, then just say the password is valid */
+        rc = 0;
+    } else {
+        decrypt_master_key(passwd, salt, encrypted_master_key, decrypted_master_key);
+        if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
+            /* They match, the password is correct */
+            rc = 0;
+        } else {
+            /* If incorrect, sleep for a bit to prevent dictionary attacks */
+            sleep(1);
+            rc = 1;
+        }
+    }
+
+    return rc;
+}
+
 /* Initialize a crypt_mnt_ftr structure.  The keysize is
  * defaulted to 16 bytes, and the filesystem size to 0.
  * Presumably, at a minimum, the caller will update the
diff --git a/cryptfs.h b/cryptfs.h
index a0693d3..1c1bc1a 100644
--- a/cryptfs.h
+++ b/cryptfs.h
@@ -75,6 +75,7 @@
 #endif
   int cryptfs_crypto_complete(void);
   int cryptfs_check_passwd(char *pw);
+  int cryptfs_verify_passwd(char *newpw);
   int cryptfs_restart(void);
   int cryptfs_enable(char *flag, char *passwd);
   int cryptfs_changepw(char *newpw);