OSDN Git Service

Encrypt on reboot
authorPaul Lawrence <paullawrence@google.com>
Fri, 20 Nov 2015 15:07:19 +0000 (07:07 -0800)
committerPaul Lawrence <paullawrence@google.com>
Mon, 23 Nov 2015 20:40:17 +0000 (12:40 -0800)
Change encryption to happen after a reboot, not before. This
removes the problem whereby if data cannot be unmounted, we cannot
encrypt.

Bug: 25426629

Change-Id: I25d610204234ed8254330d001eb965e6e87a2fe9

cryptfs.c
cryptfs.h

index d036eb2..b0aafc2 100644 (file)
--- a/cryptfs.c
+++ b/cryptfs.c
 
 #define DEFAULT_PASSWORD "default_password"
 
+#define CRYPTO_BLOCK_DEVICE "userdata"
+
+#define BREADCRUMB_FILE "/data/misc/vold/convert_fde"
+
 #define EXT4_FS 1
 #define F2FS_FS 2
 
@@ -188,6 +192,11 @@ static int keymaster_create_key(struct crypt_mnt_ftr *ftr)
     keymaster0_device_t *keymaster0_dev = 0;
     keymaster1_device_t *keymaster1_dev = 0;
 
+    if (ftr->keymaster_blob_size) {
+        SLOGI("Already have key");
+        return 0;
+    }
+
     if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) {
         SLOGE("Failed to init keymaster");
         return -1;
@@ -597,6 +606,16 @@ static int get_crypt_ftr_info(char **metadata_fname, off64_t *off)
   return rc;
 }
 
+/* Set sha256 checksum in structure */
+static void set_ftr_sha(struct crypt_mnt_ftr *crypt_ftr)
+{
+    SHA256_CTX c;
+    SHA256_Init(&c);
+    memset(crypt_ftr->sha256, 0, sizeof(crypt_ftr->sha256));
+    SHA256_Update(&c, crypt_ftr, sizeof(*crypt_ftr));
+    SHA256_Final(crypt_ftr->sha256, &c);
+}
+
 /* key or salt can be NULL, in which case just skip writing that value.  Useful to
  * update the failed mount count but not change the key.
  */
@@ -612,6 +631,8 @@ static int put_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr)
   char *fname = NULL;
   struct stat statbuf;
 
+  set_ftr_sha(crypt_ftr);
+
   if (get_crypt_ftr_info(&fname, &starting_off)) {
     SLOGE("Unable to get crypt_ftr_info\n");
     return -1;
@@ -654,6 +675,14 @@ errout:
 
 }
 
+static bool check_ftr_sha(const struct crypt_mnt_ftr *crypt_ftr)
+{
+    struct crypt_mnt_ftr copy;
+    memcpy(&copy, crypt_ftr, sizeof(copy));
+    set_ftr_sha(&copy);
+    return memcmp(copy.sha256, crypt_ftr->sha256, sizeof(copy.sha256)) == 0;
+}
+
 static inline int unix_read(int  fd, void*  buff, int  len)
 {
     return TEMP_FAILURE_RETRY(read(fd, buff, len));
@@ -2034,13 +2063,41 @@ int cryptfs_check_passwd(char *passwd)
     int rc;
 
     rc = check_unmounted_and_get_ftr(&crypt_ftr);
-    if (rc)
+    if (rc) {
+        SLOGE("Could not get footer");
         return rc;
+    }
 
     rc = test_mount_encrypted_fs(&crypt_ftr, passwd,
-                                 DATA_MNT_POINT, "userdata");
+                                 DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+    if (rc) {
+        SLOGE("Password did not match");
+        return rc;
+    }
+
+    if (crypt_ftr.flags & CRYPT_FORCE_COMPLETE) {
+        // Here we have a default actual password but a real password
+        // we must test against the scrypted value
+        // First, we must delete the crypto block device that
+        // test_mount_encrypted_fs leaves behind as a side effect
+        delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE);
+        rc = test_mount_encrypted_fs(&crypt_ftr, DEFAULT_PASSWORD,
+                                     DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+        if (rc) {
+            SLOGE("Default password did not match on reboot encryption");
+            return rc;
+        }
+
+        crypt_ftr.flags &= ~CRYPT_FORCE_COMPLETE;
+        put_crypt_ftr_and_key(&crypt_ftr);
+        rc = cryptfs_changepw(crypt_ftr.crypt_type, passwd);
+        if (rc) {
+            SLOGE("Could not change password on reboot encryption");
+            return rc;
+        }
+    }
 
-    if (rc == 0 && crypt_ftr.crypt_type != CRYPT_TYPE_DEFAULT) {
+    if (crypt_ftr.crypt_type != CRYPT_TYPE_DEFAULT) {
         cryptfs_clear_password();
         password = strdup(passwd);
         struct timespec now;
@@ -2912,6 +2969,7 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
     char key_loc[PROPERTY_VALUE_MAX];
     int num_vols;
     off64_t previously_encrypted_upto = 0;
+    bool rebootEncryption = false;
 
     if (!strcmp(howarg, "wipe")) {
       how = CRYPTO_ENABLE_WIPE;
@@ -2922,21 +2980,33 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
       goto error_unencrypted;
     }
 
-    /* See if an encryption was underway and interrupted */
     if (how == CRYPTO_ENABLE_INPLACE
-          && get_crypt_ftr_and_key(&crypt_ftr) == 0
-          && (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS)) {
-        previously_encrypted_upto = crypt_ftr.encrypted_upto;
-        crypt_ftr.encrypted_upto = 0;
-        crypt_ftr.flags &= ~CRYPT_ENCRYPTION_IN_PROGRESS;
-
-        /* At this point, we are in an inconsistent state. Until we successfully
-           complete encryption, a reboot will leave us broken. So mark the
-           encryption failed in case that happens.
-           On successfully completing encryption, remove this flag */
-        crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
+          && get_crypt_ftr_and_key(&crypt_ftr) == 0) {
+        if (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS) {
+            /* An encryption was underway and was interrupted */
+            previously_encrypted_upto = crypt_ftr.encrypted_upto;
+            crypt_ftr.encrypted_upto = 0;
+            crypt_ftr.flags &= ~CRYPT_ENCRYPTION_IN_PROGRESS;
+
+            /* At this point, we are in an inconsistent state. Until we successfully
+               complete encryption, a reboot will leave us broken. So mark the
+               encryption failed in case that happens.
+               On successfully completing encryption, remove this flag */
+            crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
+
+            put_crypt_ftr_and_key(&crypt_ftr);
+        } else if (crypt_ftr.flags & CRYPT_FORCE_ENCRYPTION) {
+            if (!check_ftr_sha(&crypt_ftr)) {
+                memset(&crypt_ftr, 0, sizeof(crypt_ftr));
+                put_crypt_ftr_and_key(&crypt_ftr);
+                goto error_unencrypted;
+            }
 
-        put_crypt_ftr_and_key(&crypt_ftr);
+            /* Doing a reboot-encryption*/
+            crypt_ftr.flags &= ~CRYPT_FORCE_ENCRYPTION;
+            crypt_ftr.flags |= CRYPT_FORCE_COMPLETE;
+            rebootEncryption = true;
+        }
     }
 
     property_get("ro.crypto.state", encrypted_state, "");
@@ -2996,13 +3066,23 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
         SLOGE("Failed to unmount all vold managed devices");
     }
 
-    /* Now unmount the /data partition. */
-    if (wait_and_unmount(DATA_MNT_POINT, false)) {
-        goto error_unencrypted;
+    /* no_ui means we are being called from init, not settings.
+       Now we always reboot from settings, so !no_ui means reboot
+     */
+    bool onlyCreateHeader = false;
+    if (!no_ui) {
+        /* Try fallback, which is to reboot and try there */
+        onlyCreateHeader = true;
+        FILE* breadcrumb = fopen(BREADCRUMB_FILE, "we");
+        if (breadcrumb == 0) {
+            SLOGE("Failed to create breadcrumb file");
+            goto error_shutting_down;
+        }
+        fclose(breadcrumb);
     }
 
     /* Do extra work for a better UX when doing the long inplace encryption */
-    if (how == CRYPTO_ENABLE_INPLACE) {
+    if (how == CRYPTO_ENABLE_INPLACE && !onlyCreateHeader) {
         /* Now that /data is unmounted, we need to mount a tmpfs
          * /data, set a property saying we're doing inplace encryption,
          * and restart the framework.
@@ -3029,7 +3109,7 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
 
     /* Start the actual work of making an encrypted filesystem */
     /* Initialize a crypt_mnt_ftr for the partition */
-    if (previously_encrypted_upto == 0) {
+    if (previously_encrypted_upto == 0 && !rebootEncryption) {
         if (cryptfs_init_crypt_mnt_ftr(&crypt_ftr)) {
             goto error_shutting_down;
         }
@@ -3044,7 +3124,11 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
            complete encryption, a reboot will leave us broken. So mark the
            encryption failed in case that happens.
            On successfully completing encryption, remove this flag */
-        crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
+        if (onlyCreateHeader) {
+            crypt_ftr.flags |= CRYPT_FORCE_ENCRYPTION;
+        } else {
+            crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
+        }
         crypt_ftr.crypt_type = crypt_type;
 #ifndef CONFIG_HW_DISK_ENCRYPTION
         strlcpy((char *)crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256", MAX_CRYPTO_TYPE_NAME_LEN);
@@ -3065,11 +3149,21 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
 #endif
 
         /* Make an encrypted master key */
-        if (create_encrypted_random_key(passwd, crypt_ftr.master_key, crypt_ftr.salt, &crypt_ftr)) {
+        if (create_encrypted_random_key(onlyCreateHeader ? DEFAULT_PASSWORD : passwd,
+                                        crypt_ftr.master_key, crypt_ftr.salt, &crypt_ftr)) {
             SLOGE("Cannot create encrypted master key\n");
             goto error_shutting_down;
         }
 
+        /* Replace scrypted intermediate key if we are preparing for a reboot */
+        if (onlyCreateHeader) {
+            unsigned char fake_master_key[KEY_LEN_BYTES];
+            unsigned char encrypted_fake_master_key[KEY_LEN_BYTES];
+            memset(fake_master_key, 0, sizeof(fake_master_key));
+            encrypt_master_key(passwd, crypt_ftr.salt, fake_master_key,
+                               encrypted_fake_master_key, &crypt_ftr);
+        }
+
         /* Write the key to the end of the partition */
         put_crypt_ftr_and_key(&crypt_ftr);
 
@@ -3088,7 +3182,12 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
         }
     }
 
-    if (how == CRYPTO_ENABLE_INPLACE && !no_ui) {
+    if (onlyCreateHeader) {
+        sleep(2);
+        cryptfs_reboot(reboot);
+    }
+
+    if (how == CRYPTO_ENABLE_INPLACE && (!no_ui || rebootEncryption)) {
         /* startup service classes main and late_start */
         property_set("vold.decrypt", "trigger_restart_min_framework");
         SLOGD("Just triggered restart_min_framework\n");
@@ -3102,7 +3201,7 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
 
     decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
     create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev,
-                          "userdata");
+                          CRYPTO_BLOCK_DEVICE);
 
     /* If we are continuing, check checksums match */
     rc = 0;
@@ -3135,7 +3234,7 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
     }
 
     /* Undo the dm-crypt mapping whether we succeed or not */
-    delete_crypto_blk_dev("userdata");
+    delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE);
 
     if (! rc) {
         /* Success */
@@ -3158,8 +3257,16 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
             /* default encryption - continue first boot sequence */
             property_set("ro.crypto.state", "encrypted");
             release_wake_lock(lockid);
-            cryptfs_check_passwd(DEFAULT_PASSWORD);
-            cryptfs_restart_internal(1);
+            if (rebootEncryption && crypt_ftr.crypt_type != CRYPT_TYPE_DEFAULT) {
+                // Bring up cryptkeeper that will check the password and set it
+                property_set("vold.decrypt", "trigger_shutdown_framework");
+                sleep(2);
+                property_set("vold.encrypt_progress", "");
+                cryptfs_trigger_restart_min_framework();
+            } else {
+                cryptfs_check_passwd(DEFAULT_PASSWORD);
+                cryptfs_restart_internal(1);
+            }
             return 0;
           } else {
             sleep(2); /* Give the UI a chance to show 100% progress */
index 1320bfe..033767f 100644 (file)
--- a/cryptfs.h
+++ b/cryptfs.h
                                         correctly marked partial encryption */
 #define CRYPT_DATA_CORRUPT 0x8 /* Set when encryption is fine, but the
                                   underlying volume is corrupt */
+#define CRYPT_FORCE_ENCRYPTION 0x10 /* Set when it is time to encrypt this
+                                       volume on boot. Everything in this
+                                       structure is set up correctly as
+                                       though device is encrypted except
+                                       that the master key is encrypted with the
+                                       default password. */
+#define CRYPT_FORCE_COMPLETE 0x20 /* Set when the above encryption cycle is
+                                     complete. On next cryptkeeper entry, match
+                                     the password. If it matches fix the master
+                                     key and remove this flag. */
 
 /* Allowed values for type in the structure below */
 #define CRYPT_TYPE_PASSWORD 0 /* master_key is encrypted with a password
@@ -94,7 +104,7 @@ struct crypt_mnt_ftr {
   __le32 keysize;       /* in bytes */
   __le32 crypt_type;    /* how master_key is encrypted. Must be a
                          * CRYPT_TYPE_XXX value */
-  __le64 fs_size;      /* Size of the encrypted fs, in 512 byte sectors */
+  __le64 fs_size;       /* Size of the encrypted fs, in 512 byte sectors */
   __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
                                   mount, set to 0 on successful mount */
   unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
@@ -145,6 +155,12 @@ struct crypt_mnt_ftr {
      then we will be OK.
    */
   unsigned char scrypted_intermediate_key[SCRYPT_LEN];
+
+  /* sha of this structure with this element set to zero
+     Used when encrypting on reboot to validate structure before doing something
+     fatal
+   */
+  unsigned char sha256[SHA256_DIGEST_LENGTH];
 };
 
 /* Persistant data that should be available before decryption.