diff --git a/ChangeLog b/ChangeLog index 8c08ecab..b049f96a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 2011-04-11 Milan Broz * Add loop manipulation code and support mapping of images in file. * Add backing device loop info into status message. + * Add luksChangeKey command. 2011-04-05 Milan Broz * Add exception to COPYING for binary distribution linked with OpenSSL library. diff --git a/lib/luks1/keymanage.c b/lib/luks1/keymanage.c index b75e6452..ae8dbd8d 100644 --- a/lib/luks1/keymanage.c +++ b/lib/luks1/keymanage.c @@ -46,6 +46,21 @@ static inline int round_up_modulo(int x, int m) { return div_round_up(x, m) * m; } +const char *dbg_slot_state(crypt_keyslot_info ki) +{ + switch(ki) { + case CRYPT_SLOT_INACTIVE: + return "INACTIVE"; + case CRYPT_SLOT_ACTIVE: + return "ACTIVE"; + case CRYPT_SLOT_ACTIVE_LAST: + return "ACTIVE_LAST"; + case CRYPT_SLOT_INVALID: + default: + return "INVALID"; + } +} + int LUKS_hdr_backup( const char *backup_file, const char *device, @@ -629,7 +644,8 @@ static int LUKS_open_key(const char *device, size_t AFEKSize; int r; - log_dbg("Trying to open key slot %d [%d].", keyIndex, (int)ki); + log_dbg("Trying to open key slot %d [%s].", keyIndex, + dbg_slot_state(ki)); if (ki < CRYPT_SLOT_ACTIVE) return -ENOENT; diff --git a/man/cryptsetup.8 b/man/cryptsetup.8 index 6b948985..136d9cbf 100644 --- a/man/cryptsetup.8 +++ b/man/cryptsetup.8 @@ -82,6 +82,20 @@ The key file with the new material is supplied as a positional argument. .IP remove supplied key or key file from LUKS device .PP +\fIluksChangeKey\fR [] +.IP +change existing key file or passphrase. An existing passphrase or key file (via \-\-key-file) must be supplied. +The key file with the new material is supplied as a positional argument. + +If no key slot is specified (and there is still free key slot on device) new slot is allocated before the old is purged. + +If \fB\-\-key\-slot\fR option is specified (or there is no free slot) command will overwrite existing slot. + +\fBWARNING:\fR Be sure you have another slot active or header backup when using explicit key slot (so you can +unlock the device even after possible media failure during slot swap). + +\fB\fR can be [\-\-key-file, \-\-keyfile-size, \-\-new-keyfile-size, \-\-key-slot]. +.PP \fIluksKillSlot\fR .IP wipe key with number from LUKS device. A remaining passphrase or @@ -258,6 +272,7 @@ set up a read-only mapping. .TP .B "\-\-iter-time, \-i" The number of milliseconds to spend with PBKDF2 password processing. This option is only relevant to the LUKS operations as \fIluksFormat\fR or \fIluksAddKey\fR. +Note that 0 means default. .TP .B "\-\-batch-mode, \-q" Do not ask for confirmation. Use with care! This option is only relevant for \fIluksFormat\fR, \fIluksAddKey\fR, \fIluksRemoveKey\fR or \fIluksKillSlot\fR. @@ -341,7 +356,7 @@ Copyright \(co 2004 Christophe Saout .br Copyright \(co 2004-2006 Clemens Fruhwirth .br -Copyright \(co 2009-2010 Red Hat, Inc. +Copyright \(co 2009-2011 Red Hat, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/src/cryptsetup.c b/src/cryptsetup.c index 317715eb..40c288d8 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -55,6 +55,7 @@ static int action_luksOpen(int arg); static int action_luksAddKey(int arg); static int action_luksKillSlot(int arg); static int action_luksRemoveKey(int arg); +static int action_luksChangeKey(int arg); static int action_isLuks(int arg); static int action_luksUUID(int arg); static int action_luksDump(int arg); @@ -81,6 +82,7 @@ static struct action_type { { "luksOpen", action_luksOpen, 0, 2, 1, N_(" "), N_("open LUKS device as mapping ") }, { "luksAddKey", action_luksAddKey, 0, 1, 1, N_(" []"), N_("add key to LUKS device") }, { "luksRemoveKey",action_luksRemoveKey, 0, 1, 1, N_(" []"), N_("removes supplied key or key file from LUKS device") }, + { "luksChangeKey",action_luksChangeKey, 0, 1, 1, N_(" []"), N_("changes supplied key or key file of LUKS device") }, { "luksKillSlot", action_luksKillSlot, 0, 2, 1, N_(" "), N_("wipes key with number from LUKS device") }, { "luksUUID", action_luksUUID, 0, 1, 0, N_(""), N_("print UUID of LUKS device") }, { "isLuks", action_isLuks, 0, 1, 0, N_(""), N_("tests for LUKS partition header") }, @@ -671,6 +673,100 @@ 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(int arg) +{ + const char *opt_new_key_file = (action_argc > 1 ? action_argv[1] : NULL); + struct crypt_device *cd = NULL; + char *vk = NULL, *password = NULL; + unsigned int passwordLen = 0; + size_t vk_size; + int new_key_slot, old_key_slot, r; + + if ((r = crypt_init(&cd, action_argv[0]))) + goto out; + + if ((r = crypt_load(cd, CRYPT_LUKS1, NULL))) + goto out; + + r = crypt_get_key(_("Enter LUKS passphrase to be changed: "), + &password, &passwordLen, + opt_keyfile_size, opt_key_file, opt_timeout, + opt_batch_mode ? 0 : opt_verify_passphrase, 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")); + 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, + opt_new_keyfile_size, opt_new_key_file, + opt_timeout, opt_batch_mode ? 0 : 1, 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")); +out: + crypt_safe_free(vk); + crypt_safe_free(password); + crypt_free(cd); + return r; +} + static int action_isLuks(int arg) { struct crypt_device *cd = NULL; diff --git a/tests/compat-test b/tests/compat-test index 228fc4a5..e92fe193 100755 --- a/tests/compat-test +++ b/tests/compat-test @@ -300,5 +300,30 @@ dmsetup resume $DEV_NAME || fail $CRYPTSETUP -q luksClose $DEV_NAME2 || fail dmsetup remove $DEV_NAME || fail +prepare "[23] ChangeKey passphrase and keyfile" wipe +# [0]$KEY1 [1]key0 +$CRYPTSETUP -q luksFormat $LOOPDEV $KEY1 --key-slot 0 || fail +echo "key0" | $CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 --key-slot 1 || fail +# keyfile [0] / keyfile [0] +$CRYPTSETUP luksChangeKey $LOOPDEV -d $KEY1 $KEY2 --key-slot 0 || fail +# passphrase [1] / passphrase [1] +echo -e "key0\nkey1\n" | $CRYPTSETUP luksChangeKey $LOOPDEV --key-slot 1 || fail +# keyfile [0] / keyfile [new] +$CRYPTSETUP luksChangeKey $LOOPDEV -d $KEY2 $KEY1 || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 0: DISABLED" || fail +# passphrase [1] / passphrase [new] +echo -e "key1\nkey0\n" | $CRYPTSETUP luksChangeKey $LOOPDEV || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 1: DISABLED" || fail +# use all slots +$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 -i 1 || fail +$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 -i 1 || fail +$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 -i 1 || fail +$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 -i 1 || fail +$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 -i 1 || fail +$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 -i 1 || fail +# still allows replace +$CRYPTSETUP luksChangeKey $LOOPDEV -d $KEY1 $KEY2 || fail +$CRYPTSETUP luksChangeKey $LOOPDEV -d $KEY1 $KEY2 2>/dev/null && fail + remove_mapping exit 0