Move change key into library (add crypt_keyslot_change_by_passphrase).
authorMilan Broz <gmazyland@gmail.com>
Fri, 7 Dec 2012 14:29:44 +0000 (15:29 +0100)
committerMilan Broz <gmazyland@gmail.com>
Fri, 7 Dec 2012 14:33:47 +0000 (15:33 +0100)
This change is useful mainly in FIPS mode, where we cannot
extract volume key directly from libcryptsetup.

TODO
lib/libcryptsetup.h
lib/libcryptsetup.sym
lib/setup.c
src/cryptsetup.c

diff --git a/TODO b/TODO
index fbccb0a..e813729 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,7 +1,4 @@
 Version 1.6.0:
 - Export wipe device functions
 - Support K/M suffixes for align payload (new switch?).
-- FIPS: move changekey to library
-- online reencryption api?
-- integrate more metadata formats
 - TRIM for keyslots
index 04c53fb..54bcbff 100644 (file)
@@ -599,6 +599,33 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd,
        size_t new_passphrase_size);
 
 /**
+ * Change defined key slot using provided passphrase
+ *
+ * @pre @e cd contains initialized and formatted LUKS device context
+ *
+ * @param cd crypt device handle
+ * @param keyslot_old old keyslot or @e CRYPT_ANY_SLOT
+ * @param keyslot_new new keyslot (can be the same as old)
+ * @param passphrase passphrase used to unlock volume key, @e NULL for query
+ * @param passphrase_size size of passphrase (binary data)
+ * @param new_passphrase passphrase for new keyslot, @e NULL for query
+ * @param new_passphrase_size size of @e new_passphrase (binary data)
+ *
+ * @return allocated key slot number or negative errno otherwise.
+ *
+ * @note This function is just internal implementation of luksChange
+ * command to avoid reading of volume key outside libcryptsetup boundary
+ * in FIPS mode.
+ */
+int crypt_keyslot_change_by_passphrase(struct crypt_device *cd,
+       int keyslot_old,
+       int keyslot_new,
+       const char *passphrase,
+       size_t passphrase_size,
+       const char *new_passphrase,
+       size_t new_passphrase_size);
+
+/**
 * Add key slot using provided key file path
  *
  * @pre @e cd contains initialized and formatted LUKS device context
index 801add6..27c5cb4 100644 (file)
@@ -26,6 +26,7 @@ CRYPTSETUP_1.0 {
                crypt_free;
 
                crypt_keyslot_add_by_passphrase;
+               crypt_keyslot_change_by_passphrase;
                crypt_keyslot_add_by_keyfile;
                crypt_keyslot_add_by_keyfile_offset;
                crypt_keyslot_add_by_volume_key;
index 995e20d..992f3c4 100644 (file)
@@ -1649,6 +1649,67 @@ out:
        return r ?: keyslot;
 }
 
+int crypt_keyslot_change_by_passphrase(struct crypt_device *cd,
+       int keyslot_old,
+       int keyslot_new,
+       const char *passphrase,
+       size_t passphrase_size,
+       const char *new_passphrase,
+       size_t new_passphrase_size)
+{
+       struct volume_key *vk = NULL;
+       int r = -EINVAL;
+
+       log_dbg("Changing passphrase from old keyslot %d to new %d.",
+               keyslot_old, keyslot_new);
+
+       if (!isLUKS(cd->type)) {
+               log_err(cd, _("This operation is supported only for LUKS device.\n"));
+               return -EINVAL;
+       }
+
+       r = LUKS_open_key_with_hdr(keyslot_old, passphrase, passphrase_size,
+                                  &cd->u.luks1.hdr, &vk, cd);
+       if (r < 0)
+               return r;
+
+       if (keyslot_old != CRYPT_ANY_SLOT && keyslot_old != r) {
+               log_dbg("Keyslot mismatch.");
+               goto out;
+       }
+       keyslot_old = r;
+
+       if (keyslot_new == CRYPT_ANY_SLOT) {
+               keyslot_new = LUKS_keyslot_find_empty(&cd->u.luks1.hdr);
+               if (keyslot_new < 0)
+                       keyslot_new = keyslot_old;
+       }
+
+       if (keyslot_old == keyslot_new) {
+               log_dbg("Key slot %d is going to be overwritten.", keyslot_old);
+               (void)crypt_keyslot_destroy(cd, keyslot_old);
+       }
+
+       r = LUKS_set_key(keyslot_new, new_passphrase, new_passphrase_size,
+                        &cd->u.luks1.hdr, vk, cd->iteration_time,
+                        &cd->u.luks1.PBKDF2_per_sec, cd);
+
+       if (keyslot_old == keyslot_new) {
+               if (r >= 0)
+                       log_verbose(cd, _("Key slot %d changed.\n"), r);
+       } else {
+               if (r >= 0) {
+                       log_verbose(cd, _("Replaced with key slot %d.\n"), r);
+                       r = crypt_keyslot_destroy(cd, keyslot_old);
+               }
+       }
+       if (r < 0)
+               log_err(cd, _("Failed to swap new key slot.\n"));
+out:
+       crypt_free_volume_key(vk);
+       return r ?: keyslot_new;
+}
+
 int crypt_keyslot_add_by_keyfile_offset(struct crypt_device *cd,
        int keyslot,
        const char *keyfile,
index 6fb3f8e..d652bad 100644 (file)
@@ -909,24 +909,13 @@ out:
        return r;
 }
 
-static int _slots_full(struct crypt_device *cd)
-{
-       int i;
-
-       for (i = 0; i < crypt_keyslot_max(crypt_get_type(cd)); i++)
-               if (crypt_keyslot_status(cd, i) == CRYPT_SLOT_INACTIVE)
-                       return 0;
-       return 1;
-}
-
 static int action_luksChangeKey(void)
 {
        const char *opt_new_key_file = (action_argc > 1 ? action_argv[1] : NULL);
        struct crypt_device *cd = NULL;
-       char *vk = NULL, *password = NULL;
-       size_t passwordLen = 0;
-       size_t vk_size;
-       int new_key_slot, old_key_slot, r;
+       char *password = NULL, *password_new = NULL;
+       size_t password_size = 0, password_new_size = 0;
+       int r;
 
        if ((r = crypt_init(&cd, uuid_or_device(action_argv[0]))))
                goto out;
@@ -938,71 +927,31 @@ static int action_luksChangeKey(void)
                crypt_set_iteration_time(cd, opt_iteration_time);
 
        r = crypt_get_key(_("Enter LUKS passphrase to be changed: "),
-                     &password, &passwordLen,
+                     &password, &password_size,
                      opt_keyfile_offset, opt_keyfile_size, opt_key_file,
                      opt_timeout, _verify_passphrase(0), cd);
        if (r < 0)
                goto out;
 
-       vk_size = crypt_get_volume_key_size(cd);
-       vk = crypt_safe_alloc(vk_size);
-       if (!vk) {
-               r = -ENOMEM;
-               goto out;
-       }
-
-       r = crypt_volume_key_get(cd, opt_key_slot, vk, &vk_size,
-                                password, passwordLen);
-       if (r < 0) {
-               if (opt_key_slot != CRYPT_ANY_SLOT)
-                       log_err(_("No key available with this passphrase.\n"));
+       /* Check password before asking for new one */
+       r = crypt_activate_by_passphrase(cd, NULL, opt_key_slot,
+                                        password, password_size, 0);
+       if (r < 0)
                goto out;
-       }
-
-       if (opt_key_slot != CRYPT_ANY_SLOT || _slots_full(cd)) {
-               log_dbg("Key slot %d is going to be overwritten (%s).",
-                       r, opt_key_slot != CRYPT_ANY_SLOT ?
-                       "explicit key slot specified" : "no free key slot");
-               old_key_slot = r;
-               new_key_slot = r;
-       } else {
-               log_dbg("Allocating new key slot.");
-               old_key_slot = r;
-               new_key_slot = CRYPT_ANY_SLOT;
-       }
 
-       crypt_safe_free(password);
-       password = NULL;
-       passwordLen = 0;
        r = crypt_get_key(_("Enter new LUKS passphrase: "),
-                         &password, &passwordLen,
+                         &password_new, &password_new_size,
                          opt_new_keyfile_offset, opt_new_keyfile_size,
                          opt_new_key_file,
                          opt_timeout, _verify_passphrase(0), cd);
        if (r < 0)
                goto out;
 
-       if (new_key_slot == old_key_slot) {
-               (void)crypt_keyslot_destroy(cd, old_key_slot);
-               r = crypt_keyslot_add_by_volume_key(cd, new_key_slot,
-                                                   vk, vk_size,
-                                                   password, passwordLen);
-               if (r >= 0)
-                       log_verbose(_("Key slot %d changed.\n"), r);
-       } else {
-               r = crypt_keyslot_add_by_volume_key(cd, CRYPT_ANY_SLOT,
-                                                   vk, vk_size,
-                                                   password, passwordLen);
-               if (r >= 0) {
-                       log_verbose(_("Replaced with key slot %d.\n"), r);
-                       r = crypt_keyslot_destroy(cd, old_key_slot);
-               }
-       }
-       if (r < 0)
-               log_err(_("Failed to swap new key slot.\n"));
+       r = crypt_keyslot_change_by_passphrase(cd, opt_key_slot, opt_key_slot,
+               password, password_size, password_new, password_new_size);
 out:
-       crypt_safe_free(vk);
        crypt_safe_free(password);
+       crypt_safe_free(password_new);
        crypt_free(cd);
        return r;
 }