diff --git a/ChangeLog b/ChangeLog index 8daa4c40..9500cfe3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ 2010-11-01 Milan Broz * No longer support luksDelKey, reload and --non-exclusive. * Remove some obsolete info from man page. + * Add crypt_get_type(), crypt_resize(), crypt_keyslot_max() + and crypt_get_active_device() to API. + * Rewrite all implementations in cryptsetup to new API. + * Fix luksRemoveKey to behave as documented (do not ask + for remaining keyslot passphrase). + * Add more regression tests for commands. 2010-10-27 Milan Broz * Rewrite cryptsetup luksFormat, luksOpen, luksAddKey to use new API diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index e27726e0..e138accd 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -144,6 +144,15 @@ int crypt_memory_lock(struct crypt_device *cd, int lock); #define CRYPT_PLAIN "PLAIN" /* regular crypt device, no on-disk header */ #define CRYPT_LUKS1 "LUKS1" /* LUKS version 1 header on-disk */ +/** + * Get device type + * + * @cd - crypt device handle + * + * Return string according to device type or NULL if not known. + */ +const char *crypt_get_type(struct crypt_device *cd); + struct crypt_params_plain { const char *hash; /* password hash function */ uint64_t offset; /* offset in sectors */ @@ -206,6 +215,19 @@ int crypt_load(struct crypt_device *cd, const char *requested_type, void *params); +/** + * Resize crypt device + * + * Returns 0 on success or negative errno value otherwise. + * + * @cd - crypt device handle + * @name - name of device to resize + * @new_size - new device size in sectors or 0 to use underlying device size + */ +int crypt_resize(struct crypt_device *cd, + const char *name, + uint64_t new_size); + /** * Suspends crypt device. * @@ -278,6 +300,16 @@ int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, const char *new_passphrase, size_t new_passphrase_size); +/** + * Get number of keyslots supported for device type. + * + * Returns slot count or negative errno otherwise if device + * doesn't not support keyslots. + * + * @type - crypt device type + */ +int crypt_keyslot_max(const char *type); + /** * Add key slot using provided key file path * @@ -336,6 +368,32 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot); #define CRYPT_ACTIVATE_READONLY (1 << 0) #define CRYPT_ACTIVATE_NO_UUID (1 << 1) +/** + * Active device runtime attributes + */ +struct crypt_active_device { + uint64_t offset; /* offset in sectors */ + uint64_t iv_offset; /* IV initilisation sector */ + uint64_t size; /* active device size */ + uint32_t flags; /* activation flags */ +}; + +/** + * Receives runtime attributes of active crypt device + * + * Returns 0 on success or negative errno value otherwise. + * + * @cd - crypt device handle + * @name - name of active device + * @cad - preallocated active device attributes to fill + * + * Note that this is old API function using global context. + * All error messages are reported also through log callback. + */ +int crypt_get_active_device(struct crypt_device *cd, + const char *name, + struct crypt_active_device *cad); + /** * Activate device or check passphrase * diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym index 10c1dd57..15368993 100644 --- a/lib/libcryptsetup.sym +++ b/lib/libcryptsetup.sym @@ -14,6 +14,7 @@ CRYPTSETUP_1.0 { crypt_memory_lock; crypt_format; crypt_load; + crypt_resize; crypt_suspend; crypt_resume_by_passphrase; crypt_resume_by_keyfile; @@ -38,9 +39,13 @@ CRYPTSETUP_1.0 { crypt_get_volume_key_size; crypt_get_device_name; + crypt_get_type; + crypt_get_active_device; + crypt_set_rng_type; crypt_get_rng_type; + crypt_keyslot_max; crypt_keyslot_status; crypt_get_error; crypt_get_dir; diff --git a/lib/setup.c b/lib/setup.c index af179fd9..8acaf4d8 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -990,8 +990,11 @@ int crypt_init(struct crypt_device **cd, const char *device) int crypt_init_by_name(struct crypt_device **cd, const char *name) { crypt_status_info ci; - char *device = NULL; - int r; + struct crypt_active_device cad; + char *device = NULL, *cipher_full = NULL, *device_uuid = NULL; + char cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; + int key_size = 0, r; + log_dbg("Allocating crypt device context by device %s.", name); @@ -1004,8 +1007,9 @@ int crypt_init_by_name(struct crypt_device **cd, const char *name) return -ENODEV; } - r = dm_query_device(name, &device, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL); + r = dm_query_device(name, &device, &cad.size, &cad.iv_offset, &cad.offset, + &cipher_full, &key_size, NULL, NULL, NULL, + &device_uuid); /* Underlying device disappeared but mapping still active */ if (r >= 0 && !device) @@ -1015,7 +1019,32 @@ int crypt_init_by_name(struct crypt_device **cd, const char *name) if (r >= 0) r = crypt_init(cd, device); + /* Try to initialise basic parameters from active device */ + if (!r && *device_uuid) { + if (!strncmp(CRYPT_PLAIN, device_uuid, sizeof(CRYPT_PLAIN)-1)) { + (*cd)->type = strdup(CRYPT_PLAIN); + (*cd)->plain_uuid = strdup(device_uuid); + (*cd)->plain_hdr.hash = NULL; /* no way to get this */ + (*cd)->plain_hdr.offset = cad.offset; + (*cd)->plain_hdr.skip = cad.iv_offset; + (*cd)->volume_key = crypt_alloc_volume_key(key_size, NULL); + if (!(*cd)->volume_key) + r = -ENOMEM; + + r = crypt_parse_name_and_mode(cipher_full, cipher, cipher_mode); + if (!r) { + (*cd)->plain_cipher = strdup(cipher); + (*cd)->plain_cipher_mode = strdup(cipher_mode); + } + } else if (!strncmp(CRYPT_LUKS1, device_uuid, sizeof(CRYPT_LUKS1)-1)) { + if (device) + r = crypt_load(*cd, CRYPT_LUKS1, NULL); + } + } + free(device); + free(cipher_full); + free(device_uuid); return r; } @@ -1186,6 +1215,53 @@ int crypt_load(struct crypt_device *cd, return r; } +int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) +{ + char *device = NULL, *cipher = NULL, *uuid = NULL, *key = NULL; + uint64_t size, skip, offset; + int key_size, read_only, r; + + /* Device context type must be initialised */ + if (!cd->type || !crypt_get_uuid(cd)) + return -EINVAL; + + r = dm_query_device(name, &device, &size, &skip, &offset, + &cipher, &key_size, &key, &read_only, NULL, &uuid); + if (r < 0) { + log_err(NULL, _("Device %s is not active.\n"), name); + goto out; + } + + if (!uuid) { + r = -EINVAL; + goto out; + } + + r = device_check_and_adjust(cd, device, &new_size, &offset, &read_only); + if (r) + goto out; + + if (new_size == size) { + log_dbg("Device has already requested size %" PRIu64 + " sectors.", size); + r = 0; + goto out; + } + + log_dbg("Resizing device %s to %" PRIu64 " sectors.", name, new_size); + + r = dm_create_device(name, device, cipher, cd->type, + crypt_get_uuid(cd), new_size, skip, offset, + key_size, key, read_only, 1); +out: + crypt_safe_free(key); + free(cipher); + free(device); + free(uuid); + + return r; +} + int crypt_set_uuid(struct crypt_device *cd, const char *uuid) { if (!isLUKS(cd->type)) { @@ -2069,6 +2145,9 @@ const char *crypt_get_uuid(struct crypt_device *cd) if (isLUKS(cd->type)) return cd->hdr.uuid; + if (isPLAIN(cd->type)) + return cd->plain_uuid; + return NULL; } @@ -2079,7 +2158,7 @@ const char *crypt_get_device_name(struct crypt_device *cd) int crypt_get_volume_key_size(struct crypt_device *cd) { - if (isPLAIN(cd->type)) + if (isPLAIN(cd->type) && cd->volume_key) return cd->volume_key->keylength; if (isLUKS(cd->type)) @@ -2108,3 +2187,32 @@ crypt_keyslot_info crypt_keyslot_status(struct crypt_device *cd, int keyslot) return LUKS_keyslot_info(&cd->hdr, keyslot); } + +int crypt_keyslot_max(const char *type) +{ + if (type && isLUKS(type)) + return LUKS_NUMKEYS; + + return -EINVAL; +} + +const char *crypt_get_type(struct crypt_device *cd) +{ + return cd->type; +} + +int crypt_get_active_device(struct crypt_device *cd, + const char *name, + struct crypt_active_device *cad) +{ + int r, readonly; + + r = dm_query_device(name, NULL, &cad->size, &cad->iv_offset, &cad->offset, + NULL, NULL, NULL, &readonly, NULL, NULL); + if (r < 0) + return r; + + cad->flags = readonly ? CRYPT_ACTIVATE_READONLY : 0; + + return 0; +} diff --git a/lib/utils_crypt.c b/lib/utils_crypt.c index ee74879f..f51fbeaa 100644 --- a/lib/utils_crypt.c +++ b/lib/utils_crypt.c @@ -22,11 +22,13 @@ int crypt_parse_name_and_mode(const char *s, char *cipher, char *cipher_mode) { if (sscanf(s, "%" MAX_CIPHER_LEN_STR "[^-]-%" MAX_CIPHER_LEN_STR "s", cipher, cipher_mode) == 2) { + if (!strcmp(cipher_mode, "plain")) + strncpy(cipher_mode, "cbc-plain", 10); return 0; } if (sscanf(s, "%" MAX_CIPHER_LEN_STR "[^-]", cipher) == 1) { - strncpy(cipher_mode, "cbc-plain", 9); + strncpy(cipher_mode, "cbc-plain", 10); return 0; } diff --git a/luks/keymanage.c b/luks/keymanage.c index 753e00f9..94db61b6 100644 --- a/luks/keymanage.c +++ b/luks/keymanage.c @@ -664,7 +664,7 @@ static int LUKS_open_key(const char *device, if(r < 0) goto out; r = LUKS_verify_volume_key(hdr, vk); - if (r >= 0) + if (!r) log_verbose(ctx, _("Key slot %d unlocked.\n"), keyIndex); out: free(AfKey); @@ -684,8 +684,10 @@ int LUKS_open_key_with_hdr(const char *device, *vk = crypt_alloc_volume_key(hdr->keyBytes, NULL); - if (keyIndex >= 0) - return LUKS_open_key(device, keyIndex, password, passwordLen, hdr, *vk, ctx); + if (keyIndex >= 0) { + r = LUKS_open_key(device, keyIndex, password, passwordLen, hdr, *vk, ctx); + return (r < 0) ? r : keyIndex; + } for(i = 0; i < LUKS_NUMKEYS; i++) { r = LUKS_open_key(device, i, password, passwordLen, hdr, *vk, ctx); diff --git a/src/cryptsetup.c b/src/cryptsetup.c index f2dc0a8f..b787d998 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -52,7 +52,6 @@ static int action_status(int arg); static int action_luksFormat(int arg); static int action_luksOpen(int arg); static int action_luksAddKey(int arg); -static int action_luksDelKey(int arg); static int action_luksKillSlot(int arg); static int action_luksRemoveKey(int arg); static int action_isLuks(int arg); @@ -116,8 +115,7 @@ static void clogger(struct crypt_device *cd, int level, const char *file, free(target); } -/* Interface Callbacks */ -static int yesDialog(char *msg) +static int _yesDialog(const char *msg, void *usrptr) { char *answer = NULL; size_t size = 0; @@ -139,7 +137,8 @@ static int yesDialog(char *msg) return r; } -static void cmdLineLog(int level, char *msg) { +static void _log(int level, const char *msg, void *usrptr) +{ switch(level) { case CRYPT_LOG_NORMAL: @@ -158,33 +157,6 @@ static void cmdLineLog(int level, char *msg) { } } -static struct interface_callbacks cmd_icb = { - .yesDialog = yesDialog, - .log = cmdLineLog, -}; - -static void _log(int level, const char *msg, void *usrptr) -{ - cmdLineLog(level, (char *)msg); -} - -static int _yesDialog(const char *msg, void *usrptr) -{ - return yesDialog((char*)msg); -} - -/* End ICBs */ - -static int check_slot(int key_slot) -{ - /* FIXME: use per format define here */ - if (key_slot != CRYPT_ANY_SLOT && (key_slot < 0 || key_slot > 8)) - return 0; - - return 1; -} - - static void show_status(int errcode) { char error[256], *error_; @@ -216,85 +188,125 @@ static void show_status(int errcode) static int action_create(int arg) { - struct crypt_options options = { - .name = action_argv[0], - .device = action_argv[1], - .cipher = opt_cipher ? opt_cipher : DEFAULT_CIPHER(PLAIN), + struct crypt_device *cd = NULL; + char cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; + struct crypt_params_plain params = { .hash = opt_hash ?: DEFAULT_PLAIN_HASH, - .key_file = opt_key_file, - .key_size = (opt_key_size ?: DEFAULT_PLAIN_KEYBITS) / 8, - .key_slot = opt_key_slot, - .flags = 0, - .size = opt_size, - .offset = opt_offset, .skip = opt_skip, - .timeout = opt_timeout, - .tries = opt_tries, - .icb = &cmd_icb, + .offset = opt_offset, }; + char *password = NULL; + unsigned int passwordLen; + int r; - if (options.hash && strcmp(options.hash, "plain") == 0) - options.hash = NULL; - if (opt_verify_passphrase) - options.flags |= CRYPT_FLAG_VERIFY; - if (opt_readonly) - options.flags |= CRYPT_FLAG_READONLY; + if (params.hash && !strcmp(params.hash, "plain")) + params.hash = NULL; - return crypt_create_device(&options); + r = crypt_parse_name_and_mode(opt_cipher ?: DEFAULT_CIPHER(PLAIN), + cipher, cipher_mode); + if (r < 0) { + log_err("No known cipher specification pattern detected.\n"); + goto out; + } + + if ((r = crypt_init(&cd, action_argv[1]))) + goto out; + + crypt_set_timeout(cd, opt_timeout); + crypt_set_password_retry(cd, opt_tries); + + r = crypt_format(cd, CRYPT_PLAIN, + cipher, cipher_mode, + NULL, NULL, + (opt_key_size ?: DEFAULT_PLAIN_KEYBITS) / 8, + ¶ms); + if (r < 0) + goto out; + + crypt_get_key(_("Enter passphrase: "), + &password, &passwordLen, + opt_keyfile_size, opt_key_file, + opt_timeout, + opt_batch_mode ? 0 : opt_verify_passphrase, + cd); + + r = crypt_activate_by_passphrase(cd, action_argv[0], CRYPT_ANY_SLOT, + password, passwordLen, + opt_readonly ? CRYPT_ACTIVATE_READONLY : 0); +out: + crypt_free(cd); + crypt_safe_free(password); + + return (r < 0) ? r : 0; } static int action_remove(int arg) { - struct crypt_options options = { - .name = action_argv[0], - .icb = &cmd_icb, - }; + struct crypt_device *cd = NULL; + int r; - return crypt_remove_device(&options); + r = crypt_init_by_name(&cd, action_argv[0]); + if (r == 0) + r = crypt_deactivate(cd, action_argv[0]); + + crypt_free(cd); + return r; } static int action_resize(int arg) { - struct crypt_options options = { - .name = action_argv[0], - .size = opt_size, - .icb = &cmd_icb, - }; + struct crypt_device *cd = NULL; + int r; - return crypt_resize_device(&options); + r = crypt_init_by_name(&cd, action_argv[0]); + if (r == 0) + r = crypt_resize(cd, action_argv[0], opt_size); + + crypt_free(cd); + return r; } static int action_status(int arg) { - struct crypt_options options = { - .name = action_argv[0], - .icb = &cmd_icb, - }; - int r; + crypt_status_info ci; + struct crypt_active_device cad; + struct crypt_device *cd = NULL; + int r = 0; - r = crypt_query_device(&options); - if (r < 0) - return r; + ci = crypt_status(NULL, action_argv[0]); + switch (ci) { + case CRYPT_INVALID: + r = -ENODEV; + break; + case CRYPT_INACTIVE: + log_std("%s/%s is inactive.\n", crypt_get_dir(), action_argv[0]); + break; + case CRYPT_ACTIVE: + case CRYPT_BUSY: + log_std("%s/%s is active%s.\n", crypt_get_dir(), action_argv[0], + ci == CRYPT_BUSY ? " and is in use" : ""); + r = crypt_init_by_name(&cd, action_argv[0]); + if (r < 0 || !crypt_get_type(cd)) + goto out; - if (r == 0) { - /* inactive */ - log_std("%s/%s is inactive.\n", crypt_get_dir(), options.name); - r = 1; - } else { - /* active */ - log_std("%s/%s is active:\n", crypt_get_dir(), options.name); - log_std(" cipher: %s\n", options.cipher); - log_std(" keysize: %d bits\n", options.key_size * 8); - log_std(" device: %s\n", options.device ?: ""); - log_std(" offset: %" PRIu64 " sectors\n", options.offset); - log_std(" size: %" PRIu64 " sectors\n", options.size); - if (options.skip) - log_std(" skipped: %" PRIu64 " sectors\n", options.skip); - log_std(" mode: %s\n", (options.flags & CRYPT_FLAG_READONLY) - ? "readonly" : "read/write"); - crypt_put_options(&options); - r = 0; + log_std(" type: %s\n", crypt_get_type(cd)); + + r = crypt_get_active_device(cd, action_argv[0], &cad); + if (r < 0) + goto out; + + log_std(" cipher: %s-%s\n", crypt_get_cipher(cd), crypt_get_cipher_mode(cd)); + log_std(" keysize: %d bits\n", crypt_get_volume_key_size(cd) * 8); + log_std(" device: %s\n", crypt_get_device_name(cd)); + log_std(" offset: %" PRIu64 " sectors\n", cad.offset); + log_std(" size: %" PRIu64 " sectors\n", cad.size); + if (cad.iv_offset) + log_std(" skipped: %" PRIu64 " sectors\n", cad.iv_offset); + log_std(" mode: %s\n", cad.flags & CRYPT_ACTIVATE_READONLY ? + "readonly" : "read/write"); } +out: + crypt_free(cd); return r; } @@ -328,7 +340,6 @@ static int action_luksFormat(int arg) { int r = -EINVAL, keysize; char *msg = NULL, *key = NULL, cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN]; - const char *key_file = NULL; char *password = NULL; unsigned int passwordLen; struct crypt_device *cd = NULL; @@ -337,19 +348,12 @@ static int action_luksFormat(int arg) .data_alignment = opt_align_payload, }; - if (action_argc > 1) { - key_file = action_argv[1]; - if (opt_key_file) - log_err(_("Option --key-file takes precedence over specified key file argument.\n")); - } else - key_file = opt_key_file; - if(asprintf(&msg, _("This will overwrite data on %s irrevocably."), action_argv[0]) == -1) { log_err(_("memory allocation error in action_luksFormat")); r = -ENOMEM; goto out; } - r = yesDialog(msg) ? 0 : -EINVAL; + r = _yesDialog(msg, NULL) ? 0 : -EINVAL; free(msg); if (r < 0) goto out; @@ -379,7 +383,7 @@ static int action_luksFormat(int arg) r = -EINVAL; crypt_get_key(_("Enter LUKS passphrase: "), &password, &passwordLen, - opt_keyfile_size, key_file, + opt_keyfile_size, opt_key_file, opt_timeout, opt_batch_mode ? 0 : 1, /* always verify */ cd); @@ -441,33 +445,143 @@ out: return (r < 0) ? r : 0; } -/* FIXME: keyslot operation needs better get_key() implementation. Use old API for now */ +static int verify_keyslot(struct crypt_device *cd, int key_slot, + char *msg_last, char *msg_pass, + const char *key_file, int keyfile_size) +{ + crypt_keyslot_info ki; + char *password = NULL; + unsigned int passwordLen, i; + int r = -EPERM; + + ki = crypt_keyslot_status(cd, key_slot); + if (ki == CRYPT_SLOT_ACTIVE_LAST && msg_last && !_yesDialog(msg_last, NULL)) + return -EPERM; + + crypt_get_key(msg_pass, &password, &passwordLen, + keyfile_size, key_file, + opt_timeout, + opt_batch_mode ? 0 : opt_verify_passphrase, + cd); + if(!password) + return -EINVAL; + + if (ki == CRYPT_SLOT_ACTIVE_LAST) { + /* check the last keyslot */ + r = crypt_activate_by_passphrase(cd, NULL, key_slot, + password, passwordLen, 0); + } else { + /* try all other keyslots */ + for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS1); i++) { + if (i == key_slot) + continue; + ki = crypt_keyslot_status(cd, key_slot); + if (ki == CRYPT_SLOT_ACTIVE) + r = crypt_activate_by_passphrase(cd, NULL, i, + password, passwordLen, 0); + if (r == i) + break; + } + } + + if (r < 0) + log_err(_("No key available with this passphrase.\n")); + + crypt_safe_free(password); + return r; +} + static int action_luksKillSlot(int arg) { - struct crypt_options options = { - .device = action_argv[0], - .key_slot = opt_key_slot, - .key_file = opt_key_file, - .timeout = opt_timeout, - .flags = !opt_batch_mode?CRYPT_FLAG_VERIFY_ON_DELKEY : 0, - .icb = &cmd_icb, - }; + struct crypt_device *cd = NULL; + int r; - return crypt_luksKillSlot(&options); + if ((r = crypt_init(&cd, action_argv[0]))) + goto out; + + crypt_set_confirm_callback(cd, _yesDialog, NULL); + crypt_set_timeout(cd, opt_timeout); + + if ((r = crypt_load(cd, CRYPT_LUKS1, NULL))) + goto out; + + switch (crypt_keyslot_status(cd, opt_key_slot)) { + case CRYPT_SLOT_ACTIVE_LAST: + case CRYPT_SLOT_ACTIVE: + log_verbose(_("Key slot %d selected for deletion.\n"), opt_key_slot); + break; + case CRYPT_SLOT_INACTIVE: + log_err(_("Key %d not active. Can't wipe.\n"), opt_key_slot); + case CRYPT_SLOT_INVALID: + goto out; + } + + if (!opt_batch_mode) { + r = verify_keyslot(cd, opt_key_slot, + _("This is the last keyslot. Device will become unusable after purging this key."), + _("Enter any remaining LUKS passphrase: "), + opt_key_file, opt_keyfile_size); + if (r < 0) + goto out; + } + + r = crypt_keyslot_destroy(cd, opt_key_slot); +out: + crypt_free(cd); + + return (r < 0) ? r : 0; } static int action_luksRemoveKey(int arg) { - struct crypt_options options = { - .device = action_argv[0], - .new_key_file = action_argc>1?action_argv[1]:NULL, - .key_file = opt_key_file, - .timeout = opt_timeout, - .flags = !opt_batch_mode?CRYPT_FLAG_VERIFY_ON_DELKEY : 0, - .icb = &cmd_icb, - }; + struct crypt_device *cd = NULL; + crypt_keyslot_info ki; + char *password = NULL; + unsigned int passwordLen; + int r; - return crypt_luksRemoveKey(&options); + if ((r = crypt_init(&cd, action_argv[0]))) + goto out; + + crypt_set_confirm_callback(cd, _yesDialog, NULL); + crypt_set_timeout(cd, opt_timeout); + + if ((r = crypt_load(cd, CRYPT_LUKS1, NULL))) + goto out; + + crypt_get_key(_("Enter LUKS passphrase to be deleted: "), + &password, &passwordLen, + opt_keyfile_size, opt_key_file, + opt_timeout, + opt_batch_mode ? 0 : opt_verify_passphrase, + cd); + if(!password) { + r = -EINVAL; + goto out; + } + + r = crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, + password, passwordLen, 0); + if (r < 0) + goto out; + + opt_key_slot = r; + log_verbose(_("Key slot %d selected for deletion.\n"), opt_key_slot); + + if (crypt_keyslot_status(cd, opt_key_slot) == CRYPT_SLOT_ACTIVE_LAST && + !_yesDialog(_("This is the last keyslot. " + "Device will become unusable after purging this key."), + NULL)) { + r = -EPERM; + goto out; + } + + r = crypt_keyslot_destroy(cd, opt_key_slot); +out: + crypt_safe_free(password); + crypt_free(cd); + + return (r < 0) ? r : 0; } static int action_luksAddKey(int arg) @@ -853,10 +967,20 @@ int main(int argc, char **argv) if (!strcmp(aname, "luksKillSlot")) opt_key_slot = atoi(action_argv[1]); - if (!check_slot(opt_key_slot)) + if (opt_key_slot != CRYPT_ANY_SLOT && + (opt_key_slot < 0 || opt_key_slot > crypt_keyslot_max(CRYPT_LUKS1))) usage(popt_context, 1, _("Key slot is invalid."), poptGetInvocationName(popt_context)); + if ((!strcmp(aname, "luksRemoveKey") || + !strcmp(aname, "luksFormat")) && + action_argc > 1) { + if (opt_key_file) + log_err(_("Option --key-file takes precedence over specified key file argument.\n")); + else + opt_key_file = (char*)action_argv[1]; + } + if (opt_random && opt_urandom) usage(popt_context, 1, _("Only one of --use-[u]random options is allowed."), poptGetInvocationName(popt_context)); diff --git a/tests/compat-test b/tests/compat-test index 8c580a2b..2afe013c 100755 --- a/tests/compat-test +++ b/tests/compat-test @@ -8,6 +8,7 @@ DEV_NAME2=dummy2 ORIG_IMG=luks-test-orig IMG=luks-test KEY1=key1 +KEY2=key2 LUKS_HEADER="S0-5 S6-7 S8-39 S40-71 S72-103 S104-107 S108-111 R112-131 R132-163 S164-167 S168-207 A0-591" KEY_SLOT0="S208-211 S212-215 R216-247 A248-251 A251-255" @@ -25,7 +26,7 @@ function remove_mapping() [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove $DEV_NAME2 [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME losetup -d $LOOPDEV >/dev/null 2>&1 - rm -f $ORIG_IMG $IMG $KEY1 >/dev/null 2>&1 + rm -f $ORIG_IMG $IMG $KEY1 $KEY2 >/dev/null 2>&1 } function fail() @@ -63,6 +64,10 @@ function prepare() dd if=/dev/urandom of=$KEY1 count=1 bs=32 >/dev/null 2>&1 fi + if [ ! -e $KEY2 ]; then + dd if=/dev/urandom of=$KEY2 count=1 bs=16 >/dev/null 2>&1 + fi + cp $IMG $ORIG_IMG [ -n "$1" ] && echo "CASE: $1" } @@ -192,5 +197,64 @@ $CRYPTSETUP -q luksFormat --master-key-file /dev/urandom -s 128 --uuid $TEST_UUI $CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME || fail $CRYPTSETUP -q luksClose $DEV_NAME || fail +prepare "[17] AddKey passphrase and keyfile" wipe +# [0]key0 [1]key1 [2]$KEY1/1 [3]$KEY1 [4]$KEY2 +$CRYPTSETUP -q luksFormat $LOOPDEV $KEY1 --key-slot 3 || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 3: ENABLED" || fail +$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 --key-slot 3 2>/dev/null && fail +# keyfile/keyfile +$CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 $KEY2 --key-slot 4 || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 4: ENABLED" || fail +# passphrase/keyfile +echo "key0" | $CRYPTSETUP luksAddKey $LOOPDEV -d $KEY1 --key-slot 0 || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 0: ENABLED" || fail +# passphrase/passphrase +echo -e "key0\nkey1\n" | $CRYPTSETUP luksAddKey $LOOPDEV --key-slot 1 || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 1: ENABLED" || fail +# keyfile/passphrase +echo -e "key1\n" | $CRYPTSETUP luksAddKey $LOOPDEV $KEY1 --key-slot 2 --new-keyfile-size 1 || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 2: ENABLED" || fail + +prepare "[18] RemoveKey passphrase and keyfile" reuse +$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY1 || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 3: DISABLED" || fail +$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY1 2>/dev/null && fail +$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY2 --keyfile-size 1 2>/dev/null && fail +$CRYPTSETUP luksRemoveKey $LOOPDEV $KEY2 || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 4: DISABLED" || fail +# kill slot using passphrase from 1 +echo "key1" | $CRYPTSETUP luksKillSlot $LOOPDEV 2 || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 2: DISABLED" || fail +# remove key0 / slot 0 +echo "key0" | $CRYPTSETUP luksRemoveKey $LOOPDEV || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 0: DISABLED" || fail +# last keyslot, in batch mode no passphrase needed... +$CRYPTSETUP luksKillSlot -q $LOOPDEV 1 || fail +$CRYPTSETUP luksDump $LOOPDEV | grep -q "Key Slot 1: DISABLED" || fail + +prepare "[19] create & status & resize" wipe +echo "key0" | $CRYPTSETUP create $DEV_NAME $LOOPDEV --hash xxx 2>/dev/null && fail +echo "key0" | $CRYPTSETUP create $DEV_NAME $LOOPDEV --hash sha1 --cipher aes-cbc-essiv:sha256 --offset 3 --skip 4 --readonly || fail +$CRYPTSETUP -q status $DEV_NAME | grep "offset:" | grep -q "3 sectors" || fail +$CRYPTSETUP -q status $DEV_NAME | grep "skipped:" | grep -q "4 sectors" || fail +$CRYPTSETUP -q status $DEV_NAME | grep "mode:" | grep -q "readonly" || fail +$CRYPTSETUP -q resize $DEV_NAME --size 100 || fail +$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "100 sectors" || fail +$CRYPTSETUP -q resize $DEV_NAME || fail +$CRYPTSETUP -q status $DEV_NAME | grep "size:" | grep -q "19997 sectors" || fail +$CRYPTSETUP -q remove $DEV_NAME || fail +echo "key0" | $CRYPTSETUP create $DEV_NAME $LOOPDEV || fail +$CRYPTSETUP -q remove $DEV_NAME || fail +echo "key0" | $CRYPTSETUP -q create $DEV_NAME $LOOPDEV || fail +$CRYPTSETUP -q remove $DEV_NAME || fail +# verify is ignored on non-tty input +echo "key0" | $CRYPTSETUP create $DEV_NAME $LOOPDEV --verify-passphrase || fail +$CRYPTSETUP -q remove $DEV_NAME || fail +$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 --key-size 255 2>/dev/null && fail +$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 || fail +$CRYPTSETUP create $DEV_NAME $LOOPDEV -d $KEY1 2>/dev/null && fail +$CRYPTSETUP create $DEV_NAME $LOOPDEV -d blah 2>/dev/null && fail +$CRYPTSETUP -q remove $DEV_NAME || fail + remove_mapping exit 0