+ r = tools_get_key(_("Enter passphrase to protect and upgrade reencryption metadata: "),
+ &password, &passwordLen, ARG_UINT64(OPT_KEYFILE_OFFSET_ID),
+ ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID), ARG_UINT32(OPT_TIMEOUT_ID),
+ verify_passphrase(0), 0, cd);
+ if (r < 0)
+ return r;
+
+ r = crypt_reencrypt_init_by_passphrase(cd, NULL, password, passwordLen,
+ ARG_INT32(OPT_KEY_SLOT_ID), ARG_INT32(OPT_KEY_SLOT_ID), NULL, NULL, ¶ms);
+ tools_passphrase_msg(r);
+ if (r < 0)
+ goto out;
+
+ r = crypt_activate_by_passphrase(cd, NULL, ARG_INT32(OPT_KEY_SLOT_ID),
+ password, passwordLen, 0);
+ tools_passphrase_msg(r);
+ if (r >= 0)
+ r = 0;
+
+out:
+ crypt_safe_free(password);
+ return r;
+}
+
+static int luks2_reencrypt_repair(struct crypt_device *cd)
+{
+ int r;
+ size_t passwordLen;
+ const char *msg;
+ char *password = NULL;
+ struct crypt_params_reencrypt params = {};
+
+ crypt_reencrypt_info ri = crypt_reencrypt_status(cd, ¶ms);
+
+ if (params.flags & CRYPT_REENCRYPT_REPAIR_NEEDED)
+ return reencrypt_metadata_repair(cd);
+
+ switch (ri) {
+ case CRYPT_REENCRYPT_NONE:
+ return 0;
+ case CRYPT_REENCRYPT_CLEAN:
+ break;
+ case CRYPT_REENCRYPT_CRASH:
+ if (!ARG_SET(OPT_BATCH_MODE_ID) &&
+ !yesDialog(_("Really proceed with LUKS2 reencryption recovery?"),
+ _("Operation aborted.\n")))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ri == CRYPT_REENCRYPT_CLEAN)
+ msg = _("Enter passphrase to verify reencryption metadata digest: ");
+ else
+ msg = _("Enter passphrase for reencryption recovery: ");
+
+ r = tools_get_key(msg, &password, &passwordLen, ARG_UINT64(OPT_KEYFILE_OFFSET_ID),
+ ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID), ARG_UINT32(OPT_TIMEOUT_ID),
+ verify_passphrase(0), 0, cd);
+ if (r < 0)
+ return r;
+
+ r = crypt_activate_by_passphrase(cd, NULL, ARG_INT32(OPT_KEY_SLOT_ID),
+ password, passwordLen, 0);
+ if (r < 0)
+ goto out;
+
+ if (ri == CRYPT_REENCRYPT_CLEAN) {
+ r = 0;
+ goto out;
+ }
+
+ r = crypt_reencrypt_init_by_passphrase(cd, NULL, password, passwordLen,
+ ARG_INT32(OPT_KEY_SLOT_ID), ARG_INT32(OPT_KEY_SLOT_ID), NULL, NULL,
+ &(struct crypt_params_reencrypt){ .flags = CRYPT_REENCRYPT_RECOVERY });
+ if (r > 0)
+ r = 0;
+out:
+ crypt_safe_free(password);
+
+ return r;
+}
+
+static int action_luksRepair(void)
+{
+ struct crypt_device *cd = NULL;
+ int r;
+
+ if ((r = crypt_init_data_device(&cd, ARG_STR(OPT_HEADER_ID) ?: action_argv[0],
+ action_argv[0])))
+ goto out;
+
+ crypt_set_log_callback(cd, quiet_log, &log_parms);
+ r = crypt_load(cd, luksType(device_type), NULL);
+ crypt_set_log_callback(cd, tool_log, &log_parms);
+ if (r == 0 && isLUKS2(crypt_get_type(cd))) {
+ /*
+ * LUKS2 triggers autorepair in crypt_load() above
+ * LUKS1 need to call crypt_repair() even if crypt_load() is ok
+ */
+ log_verbose(_("No known problems detected for LUKS header."));
+ goto out;
+ }
+
+ r = tools_detect_signatures(action_argv[0], PRB_FILTER_LUKS, NULL, ARG_SET(OPT_BATCH_MODE_ID));
+ if (r < 0)
+ goto out;
+
+ if (!ARG_SET(OPT_BATCH_MODE_ID) &&
+ !yesDialog(_("Really try to repair LUKS device header?"),
+ _("Operation aborted.\n")))
+ r = -EINVAL;
+ else
+ r = crypt_repair(cd, luksType(device_type), NULL);
+out:
+ /* Header is ok, check if reencryption metadata needs repair/recovery. */
+ if (!r && isLUKS2(crypt_get_type(cd)))
+ r = luks2_reencrypt_repair(cd);
+
+ crypt_free(cd);
+ return r;
+}
+
+static int _wipe_data_device(struct crypt_device *cd)
+{
+ char tmp_name[64], tmp_path[128], tmp_uuid[40];
+ uuid_t tmp_uuid_bin;
+ int r = -EINVAL;
+ char *backing_file = NULL;
+ struct tools_progress_params prog_parms = {
+ .frequency = ARG_UINT32(OPT_PROGRESS_FREQUENCY_ID),
+ .batch_mode = ARG_SET(OPT_BATCH_MODE_ID),
+ .json_output = ARG_SET(OPT_PROGRESS_JSON_ID),
+ .interrupt_message = _("\nWipe interrupted."),
+ .device = tools_get_device_name(crypt_get_device_name(cd), &backing_file)
+ };
+
+ if (!ARG_SET(OPT_BATCH_MODE_ID))
+ log_std(_("Wiping device to initialize integrity checksum.\n"
+ "You can interrupt this by pressing CTRL+c "
+ "(rest of not wiped device will contain invalid checksum).\n"));
+
+ /* Activate the device a temporary one */
+ uuid_generate(tmp_uuid_bin);
+ uuid_unparse(tmp_uuid_bin, tmp_uuid);
+ if (snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid) < 0)
+ goto out;
+ if (snprintf(tmp_path, sizeof(tmp_path), "%s/%s", crypt_get_dir(), tmp_name) < 0)
+ goto out;
+
+ r = crypt_activate_by_volume_key(cd, tmp_name, NULL, 0,
+ CRYPT_ACTIVATE_PRIVATE | CRYPT_ACTIVATE_NO_JOURNAL);
+ if (r < 0)
+ goto out;
+
+ /* Wipe the device */
+ set_int_handler(0);
+ r = crypt_wipe(cd, tmp_path, CRYPT_WIPE_ZERO, 0, 0, DEFAULT_WIPE_BLOCK,
+ 0, &tools_progress, &prog_parms);
+ if (crypt_deactivate(cd, tmp_name))
+ log_err(_("Cannot deactivate temporary device %s."), tmp_path);
+ set_int_block(0);
+
+out:
+ free(backing_file);
+ return r;
+}
+
+static int strcmp_or_null(const char *str, const char *expected)
+{
+ return !str ? 0 : strcmp(str, expected);
+}
+
+int luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_passwordLen)
+{
+ int r = -EINVAL, keysize, integrity_keysize = 0, fd, created = 0;
+ struct stat st;
+ const char *header_device, *type;
+ char *msg = NULL, *key = NULL, *password = NULL;
+ char cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN], integrity[MAX_CIPHER_LEN];
+ size_t passwordLen, signatures;
+ struct crypt_device *cd = NULL;
+ struct crypt_params_luks1 params1 = {
+ .hash = ARG_STR(OPT_HASH_ID) ?: DEFAULT_LUKS1_HASH,
+ .data_alignment = ARG_UINT32(OPT_ALIGN_PAYLOAD_ID),
+ .data_device = ARG_SET(OPT_HEADER_ID) ? action_argv[0] : NULL,
+ };
+ struct crypt_params_luks2 params2 = {
+ .data_alignment = params1.data_alignment,
+ .data_device = params1.data_device,
+ .sector_size = ARG_UINT32(OPT_SECTOR_SIZE_ID),
+ .label = ARG_STR(OPT_LABEL_ID),
+ .subsystem = ARG_STR(OPT_SUBSYSTEM_ID)
+ };
+ void *params;
+
+ type = luksType(device_type);
+ if (!type)
+ type = crypt_get_default_type();
+
+ if (isLUKS2(type)) {
+ params = ¶ms2;
+ } else if (isLUKS1(type)) {
+ params = ¶ms1;
+
+ if (ARG_UINT32(OPT_SECTOR_SIZE_ID) > SECTOR_SIZE) {
+ log_err(_("Unsupported encryption sector size."));
+ return -EINVAL;
+ }
+
+ if (ARG_SET(OPT_INTEGRITY_ID)) {
+ log_err(_("Integrity option can be used only for LUKS2 format."));
+ return -EINVAL;
+ }
+
+ if (ARG_SET(OPT_LUKS2_KEYSLOTS_SIZE_ID) || ARG_SET(OPT_LUKS2_METADATA_SIZE_ID)) {
+ log_err(_("Unsupported LUKS2 metadata size options."));
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
+
+ /* Create header file (must contain at least one sector)? */
+ if (ARG_SET(OPT_HEADER_ID) && stat(ARG_STR(OPT_HEADER_ID), &st) < 0 && errno == ENOENT) {
+ if (!ARG_SET(OPT_BATCH_MODE_ID) &&
+ !yesDialog(_("Header file does not exist, do you want to create it?"),
+ _("Operation aborted.\n")))
+ return -EPERM;
+
+ log_dbg("Creating header file.");
+ /* coverity[toctou] */
+ fd = open(ARG_STR(OPT_HEADER_ID), O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (fd == -1 || posix_fallocate(fd, 0, 4096))
+ log_err(_("Cannot create header file %s."), ARG_STR(OPT_HEADER_ID));
+ else {
+ r = 0;
+ created = 1;
+ }
+ if (fd != -1)
+ close(fd);
+ if (r < 0)
+ return r;
+ }
+
+ header_device = ARG_STR(OPT_HEADER_ID) ?: action_argv[0];
+
+ r = crypt_parse_name_and_mode(ARG_STR(OPT_CIPHER_ID) ?: DEFAULT_CIPHER(LUKS1),
+ cipher, NULL, cipher_mode);
+ if (r < 0) {
+ log_err(_("No known cipher specification pattern detected."));
+ goto out;
+ }
+
+ if (ARG_SET(OPT_INTEGRITY_ID)) {
+ r = crypt_parse_integrity_mode(ARG_STR(OPT_INTEGRITY_ID), integrity, &integrity_keysize);
+ if (r < 0) {
+ log_err(_("No known integrity specification pattern detected."));
+ goto out;
+ }
+ params2.integrity = integrity;
+ /* FIXME: we use default integrity_params (set to NULL) */
+ }
+
+ /* Never call pwquality if using null cipher */
+ if (crypt_is_cipher_null(cipher))
+ ARG_SET_TRUE(OPT_FORCE_PASSWORD_ID);
+
+ if ((r = crypt_init(&cd, header_device))) {
+ if (ARG_SET(OPT_HEADER_ID))
+ log_err(_("Cannot use %s as on-disk header."), header_device);
+ return r;
+ }
+
+ if (ARG_SET(OPT_LUKS2_KEYSLOTS_SIZE_ID) || ARG_SET(OPT_LUKS2_METADATA_SIZE_ID)) {
+ r = crypt_set_metadata_size(cd, ARG_UINT64(OPT_LUKS2_METADATA_SIZE_ID), ARG_UINT64(OPT_LUKS2_KEYSLOTS_SIZE_ID));
+ if (r < 0) {
+ log_err(_("Unsupported LUKS2 metadata size options."));
+ goto out;
+ }
+ }
+
+ if (ARG_SET(OPT_OFFSET_ID)) {
+ r = crypt_set_data_offset(cd, ARG_UINT64(OPT_OFFSET_ID));
+ if (r < 0)
+ goto out;
+ }
+
+ /* Print all present signatures in read-only mode */
+ r = tools_detect_signatures(header_device, PRB_FILTER_NONE, &signatures, ARG_SET(OPT_BATCH_MODE_ID));
+ if (r < 0)
+ goto out;
+
+ if (!created && !ARG_SET(OPT_BATCH_MODE_ID)) {
+ r = asprintf(&msg, _("This will overwrite data on %s irrevocably."), header_device);
+ if (r == -1) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ r = yesDialog(msg, _("Operation aborted.\n")) ? 0 : -EINVAL;
+ free(msg);
+ if (r < 0)
+ goto out;
+ }
+
+ keysize = get_adjusted_key_size(cipher_mode, DEFAULT_LUKS1_KEYBITS, integrity_keysize);
+
+ if (ARG_SET(OPT_USE_RANDOM_ID))
+ crypt_set_rng_type(cd, CRYPT_RNG_RANDOM);
+ else if (ARG_SET(OPT_USE_URANDOM_ID))
+ crypt_set_rng_type(cd, CRYPT_RNG_URANDOM);
+
+ r = tools_get_key(NULL, &password, &passwordLen,
+ ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID),
+ ARG_UINT32(OPT_TIMEOUT_ID), verify_passphrase(1), !ARG_SET(OPT_FORCE_PASSWORD_ID), cd);
+ if (r < 0)
+ goto out;
+
+ if (ARG_SET(OPT_VOLUME_KEY_FILE_ID)) {
+ r = tools_read_vk(ARG_STR(OPT_VOLUME_KEY_FILE_ID), &key, keysize);
+ if (r < 0)
+ goto out;
+ }
+
+ r = set_pbkdf_params(cd, type);
+ if (r) {
+ log_err(_("Failed to set pbkdf parameters."));
+ goto out;
+ }
+
+ /* Signature candidates found */
+ if (signatures && ((r = tools_wipe_all_signatures(header_device, true, false)) < 0))
+ goto out;
+
+ if (ARG_SET(OPT_INTEGRITY_LEGACY_PADDING_ID))
+ crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_PADDING);
+
+ r = crypt_format(cd, type, cipher, cipher_mode,
+ ARG_STR(OPT_UUID_ID), key, keysize, params);
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+
+ r = _set_keyslot_encryption_params(cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_keyslot_add_by_volume_key(cd, ARG_INT32(OPT_KEY_SLOT_ID),
+ key, keysize,
+ password, passwordLen);
+ if (r < 0) {
+ (void) tools_wipe_all_signatures(header_device, true, false);
+ goto out;
+ }
+ tools_keyslot_msg(r, CREATED);
+
+ if (ARG_SET(OPT_INTEGRITY_ID) && !ARG_SET(OPT_INTEGRITY_NO_WIPE_ID) &&
+ strcmp_or_null(params2.integrity, "none"))
+ r = _wipe_data_device(cd);
+out:
+ if (r >= 0 && r_cd && r_password && r_passwordLen) {
+ *r_cd = cd;
+ *r_password = password;
+ *r_passwordLen = passwordLen;
+ } else {
+ crypt_free(cd);
+ crypt_safe_free(password);
+ }
+
+ crypt_safe_free(key);
+
+ return r;
+}
+
+static int action_luksFormat(void)
+{
+ return luksFormat(NULL, NULL, NULL);
+}
+
+static int action_open_luks(void)
+{
+ struct crypt_active_device cad;
+ struct crypt_device *cd = NULL;
+ const char *data_device, *header_device, *activated_name;
+ char *key = NULL;
+ uint32_t activate_flags = 0;
+ int r, keysize, tries;
+ char *password = NULL;
+ size_t passwordLen;
+ struct stat st;
+
+ if (ARG_SET(OPT_REFRESH_ID)) {
+ activated_name = action_argc > 1 ? action_argv[1] : action_argv[0];
+ r = crypt_init_by_name_and_header(&cd, activated_name, ARG_STR(OPT_HEADER_ID));
+ if (r)
+ goto out;
+ activate_flags |= CRYPT_ACTIVATE_REFRESH;
+ } else {
+ header_device = uuid_or_device_header(&data_device);
+
+ activated_name = ARG_SET(OPT_TEST_PASSPHRASE_ID) ? NULL : action_argv[1];
+
+ if ((r = crypt_init_data_device(&cd, header_device, data_device)))
+ goto out;
+
+ if ((r = crypt_load(cd, luksType(device_type), NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ header_device);
+ goto out;
+ }
+
+ if (!data_device && (crypt_get_data_offset(cd) < 8) && !ARG_SET(OPT_TEST_PASSPHRASE_ID)) {
+ log_err(_("Reduced data offset is allowed only for detached LUKS header."));
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (activated_name && !stat(crypt_get_device_name(cd), &st) && S_ISREG(st.st_mode) &&
+ crypt_get_data_offset(cd) >= ((uint64_t)st.st_size / SECTOR_SIZE)) {
+ log_err(_("LUKS file container %s is too small for activation, there is no remaining space for data."),
+ crypt_get_device_name(cd));
+ r = -EINVAL;
+ goto out;
+ }
+ }
+
+ set_activation_flags(&activate_flags);
+
+ if (ARG_SET(OPT_VOLUME_KEY_FILE_ID)) {
+ keysize = crypt_get_volume_key_size(cd);
+ if (!keysize && !ARG_SET(OPT_KEY_SIZE_ID)) {
+ log_err(_("Cannot determine volume key size for LUKS without keyslots, please use --key-size option."));
+ r = -EINVAL;
+ goto out;
+ } else if (!keysize)
+ keysize = ARG_UINT32(OPT_KEY_SIZE_ID) / 8;
+
+ r = tools_read_vk(ARG_STR(OPT_VOLUME_KEY_FILE_ID), &key, keysize);
+ if (r < 0)
+ goto out;
+ r = crypt_activate_by_volume_key(cd, activated_name,
+ key, keysize, activate_flags);
+ } else {
+ r = crypt_activate_by_token_pin(cd, activated_name, ARG_STR(OPT_TOKEN_TYPE_ID),
+ ARG_INT32(OPT_TOKEN_ID_ID), NULL, 0, NULL, activate_flags);
+ tools_keyslot_msg(r, UNLOCKED);
+ tools_token_error_msg(r, ARG_STR(OPT_TOKEN_TYPE_ID), ARG_INT32(OPT_TOKEN_ID_ID), false);
+
+ /* Token requires PIN. Ask if there is evident preference for tokens */
+ if (r == -ENOANO && (ARG_SET(OPT_TOKEN_ONLY_ID) || ARG_SET(OPT_TOKEN_TYPE_ID) ||
+ ARG_SET(OPT_TOKEN_ID_ID)))
+ r = _try_token_pin_unlock(cd, ARG_INT32(OPT_TOKEN_ID_ID), activated_name, ARG_STR(OPT_TOKEN_TYPE_ID), activate_flags, set_tries_tty(), true);
+
+ if (r >= 0 || r == -EEXIST || quit || ARG_SET(OPT_TOKEN_ONLY_ID))
+ goto out;
+
+ tries = set_tries_tty();
+ do {
+ r = tools_get_key(NULL, &password, &passwordLen,
+ ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID),
+ ARG_UINT32(OPT_TIMEOUT_ID), verify_passphrase(0), 0, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_activate_by_passphrase(cd, activated_name,
+ ARG_INT32(OPT_KEY_SLOT_ID), password, passwordLen, activate_flags);
+ tools_keyslot_msg(r, UNLOCKED);
+ tools_passphrase_msg(r);
+ check_signal(&r);
+ crypt_safe_free(password);
+ password = NULL;
+ } while ((r == -EPERM || r == -ERANGE) && (--tries > 0));
+ }
+out:
+ if (r >= 0 && ARG_SET(OPT_PERSISTENT_ID) &&
+ (crypt_get_active_device(cd, activated_name, &cad) ||
+ crypt_persistent_flags_set(cd, CRYPT_FLAGS_ACTIVATION, cad.flags & activate_flags)))
+ log_err(_("Device activated but cannot make flags persistent."));
+
+ crypt_safe_free(key);
+ crypt_safe_free(password);
+ crypt_free(cd);
+ return r;
+}
+
+static int verify_keyslot(struct crypt_device *cd, int key_slot, crypt_keyslot_info ki,
+ char *msg_last, char *msg_pass, char *msg_fail,
+ const char *key_file, uint64_t keyfile_offset,
+ int keyfile_size)
+{
+ char *password = NULL;
+ size_t passwordLen;
+ int i, max, r;
+
+ if (ki == CRYPT_SLOT_ACTIVE_LAST && !ARG_SET(OPT_BATCH_MODE_ID) && !key_file &&
+ msg_last && !ARG_SET(OPT_BATCH_MODE_ID) && !yesDialog(msg_last, msg_fail))
+ return -EPERM;
+
+ r = tools_get_key(msg_pass, &password, &passwordLen,
+ keyfile_offset, keyfile_size, key_file, ARG_UINT32(OPT_TIMEOUT_ID),
+ verify_passphrase(0), 0, cd);
+ if (r < 0)
+ goto out;
+
+ 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 */
+ r = crypt_keyslot_max(crypt_get_type(cd));
+ if (r < 0)
+ goto out;
+ max = r;
+
+ for (i = 0; i < max ; i++) {
+ if (i == key_slot)
+ continue;
+ ki = crypt_keyslot_status(cd, i);
+ if (ki == CRYPT_SLOT_ACTIVE || ki == CRYPT_SLOT_ACTIVE_LAST)
+ r = crypt_activate_by_passphrase(cd, NULL, i,
+ password, passwordLen, 0);
+ if (r == i)
+ break;
+ }
+ }
+
+ /* Handle inactive keyslots the same as bad password here */
+ if (r == -ENOENT)
+ r = -EPERM;
+ tools_passphrase_msg(r);
+out:
+ crypt_safe_free(password);
+ return r;
+}
+
+static int action_luksKillSlot(void)
+{
+ struct crypt_device *cd = NULL;
+ crypt_keyslot_info ki;
+ int r;
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ if ((r = crypt_load(cd, luksType(device_type), NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ ki = crypt_keyslot_status(cd, ARG_INT32(OPT_KEY_SLOT_ID));
+ switch (ki) {
+ case CRYPT_SLOT_ACTIVE_LAST:
+ case CRYPT_SLOT_ACTIVE:
+ case CRYPT_SLOT_UNBOUND:
+ log_verbose(_("Keyslot %d is selected for deletion."), ARG_INT32(OPT_KEY_SLOT_ID));
+ break;
+ case CRYPT_SLOT_INACTIVE:
+ log_err(_("Keyslot %d is not active."), ARG_INT32(OPT_KEY_SLOT_ID));
+ /* fall through */
+ case CRYPT_SLOT_INVALID:
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (!ARG_SET(OPT_BATCH_MODE_ID) || ARG_SET(OPT_KEY_FILE_ID) || !isatty(STDIN_FILENO)) {
+ r = verify_keyslot(cd, ARG_INT32(OPT_KEY_SLOT_ID), ki,
+ _("This is the last keyslot. Device will become unusable after purging this key."),
+ _("Enter any remaining passphrase: "),
+ _("Operation aborted, the keyslot was NOT wiped.\n"),
+ ARG_STR(OPT_KEY_FILE_ID), ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID));
+ tools_keyslot_msg(r, UNLOCKED);
+
+ if (r == -EPIPE && (!ARG_SET(OPT_KEY_FILE_ID) || tools_is_stdin(ARG_STR(OPT_KEY_FILE_ID)))) {
+ log_dbg("Failed read from input, ignoring passphrase.");
+ r = 0;
+ }
+
+ if (r < 0)
+ goto out;
+ }
+
+ r = crypt_keyslot_destroy(cd, ARG_INT32(OPT_KEY_SLOT_ID));
+ tools_keyslot_msg(ARG_INT32(OPT_KEY_SLOT_ID), REMOVED);
+out:
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksRemoveKey(void)
+{
+ struct crypt_device *cd = NULL;
+ char *password = NULL;
+ size_t passwordLen;
+ int r;
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ if ((r = crypt_load(cd, luksType(device_type), NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ r = tools_get_key(_("Enter passphrase to be deleted: "),
+ &password, &passwordLen,
+ ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID),
+ ARG_UINT32(OPT_TIMEOUT_ID),
+ verify_passphrase(0), 0,
+ cd);
+ if(r < 0)
+ goto out;
+
+ r = crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT,
+ password, passwordLen, 0);
+ tools_passphrase_msg(r);
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+ tools_keyslot_msg(r, UNLOCKED);
+
+ ARG_SET_INT32(OPT_KEY_SLOT_ID, r);
+ log_verbose(_("Keyslot %d is selected for deletion."), ARG_INT32(OPT_KEY_SLOT_ID));
+
+ if (crypt_keyslot_status(cd, ARG_INT32(OPT_KEY_SLOT_ID)) == CRYPT_SLOT_ACTIVE_LAST &&
+ !ARG_SET(OPT_BATCH_MODE_ID) &&
+ !yesDialog(_("This is the last keyslot. "
+ "Device will become unusable after purging this key."),
+ _("Operation aborted, the keyslot was NOT wiped.\n"))) {
+ r = -EPERM;
+ goto out;
+ }
+
+ r = crypt_keyslot_destroy(cd, ARG_INT32(OPT_KEY_SLOT_ID));
+ tools_keyslot_msg(ARG_INT32(OPT_KEY_SLOT_ID), REMOVED);
+out:
+ crypt_safe_free(password);
+ crypt_free(cd);
+ return r;
+}
+
+static int luksAddUnboundKey(void)
+{
+ int r = -EINVAL, keysize = 0;
+ char *key = NULL;
+ const char *new_key_file = (action_argc > 1 ? action_argv[1] : NULL);
+ char *password_new = NULL;
+ size_t password_new_size = 0;
+ struct crypt_device *cd = NULL;
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ if ((r = crypt_load(cd, CRYPT_LUKS2, NULL))) {
+ log_err(_("Device %s is not a valid LUKS2 device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ r = _set_keyslot_encryption_params(cd);
+ if (r < 0)
+ goto out;
+
+ /* Never call pwquality if using null cipher */
+ if (crypt_is_cipher_null(crypt_get_cipher(cd)))
+ ARG_SET_TRUE(OPT_FORCE_PASSWORD_ID);
+
+ keysize = ARG_UINT32(OPT_KEY_SIZE_ID) / 8;
+ r = set_pbkdf_params(cd, crypt_get_type(cd));
+ if (r) {
+ log_err(_("Failed to set pbkdf parameters."));
+ goto out;
+ }
+
+ if (ARG_SET(OPT_VOLUME_KEY_FILE_ID)) {
+ r = tools_read_vk(ARG_STR(OPT_VOLUME_KEY_FILE_ID), &key, keysize);
+ if (r < 0)
+ goto out;
+
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+ }
+
+ r = tools_get_key(_("Enter new passphrase for key slot: "),
+ &password_new, &password_new_size,
+ ARG_UINT64(OPT_NEW_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_NEW_KEYFILE_SIZE_ID),
+ new_key_file, ARG_UINT32(OPT_TIMEOUT_ID),
+ verify_passphrase(1), !ARG_SET(OPT_FORCE_PASSWORD_ID), cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_keyslot_add_by_key(cd, ARG_INT32(OPT_KEY_SLOT_ID), key, keysize,
+ password_new, password_new_size, CRYPT_VOLUME_KEY_NO_SEGMENT);
+ tools_keyslot_msg(r, CREATED);
+out:
+ crypt_safe_free(password_new);
+ crypt_safe_free(key);
+ crypt_free(cd);
+ return r;
+}
+
+static int _ask_for_pin(struct crypt_device *cd,
+ int token_id, char **r_pin, size_t *r_pin_size,
+ struct crypt_keyslot_context *kc)
+{
+ int r;
+ char msg[64];
+
+ assert(r_pin);
+ assert(r_pin_size);
+ assert(kc);
+ assert(token_id >= 0 || token_id == CRYPT_ANY_TOKEN);
+
+ if (crypt_keyslot_context_get_type(kc) != CRYPT_KC_TYPE_TOKEN)
+ return -EINVAL;
+
+ if (token_id == CRYPT_ANY_TOKEN)
+ r = snprintf(msg, sizeof(msg), _("Enter token PIN: "));
+ else
+ r = snprintf(msg, sizeof(msg), _("Enter token %d PIN: "), token_id);
+ if (r < 0 || (size_t)r >= sizeof(msg))
+ return -EINVAL;
+
+ r = tools_get_key(msg, r_pin, r_pin_size, 0, 0, NULL,
+ ARG_UINT32(OPT_TIMEOUT_ID), verify_passphrase(0), 0, cd);
+ if (r < 0)
+ return r;
+
+ r = crypt_keyslot_context_set_pin(cd, *r_pin, *r_pin_size, kc);
+ if (r < 0) {
+ crypt_safe_free(*r_pin);
+ *r_pin = NULL;
+ *r_pin_size = 0;
+ }
+
+ return r;
+}
+
+static int try_keyslot_add(struct crypt_device *cd,
+ int keyslot_existing,
+ int keyslot_new,
+ struct crypt_keyslot_context *kc,
+ struct crypt_keyslot_context *kc_new,
+ bool pin_provided,
+ bool new_pin_provided)
+{
+ int r;
+
+ r = crypt_keyslot_add_by_keyslot_context(cd, keyslot_existing, kc, keyslot_new, kc_new, 0);
+ if (crypt_keyslot_context_get_type(kc) == CRYPT_KC_TYPE_TOKEN)
+ tools_token_error_msg(crypt_keyslot_context_get_error(kc), ARG_STR(OPT_TOKEN_TYPE_ID),
+ ARG_INT32(OPT_TOKEN_ID_ID), pin_provided);
+ if (crypt_keyslot_context_get_type(kc_new) == CRYPT_KC_TYPE_TOKEN)
+ tools_token_error_msg(crypt_keyslot_context_get_error(kc_new), NULL,
+ ARG_INT32(OPT_NEW_TOKEN_ID_ID), new_pin_provided);
+ return r;
+}
+
+static int action_luksAddKey(void)
+{
+ int keyslot_old, keyslot_new, keysize = 0, r = -EINVAL;
+ const char *new_key_file = (action_argc > 1 ? action_argv[1] : NULL);
+ char *key = NULL, *password = NULL, *password_new = NULL, *pin = NULL, *pin_new = NULL;
+ size_t pin_size, pin_size_new, password_size = 0, password_new_size = 0;
+ struct crypt_device *cd = NULL;
+ struct crypt_keyslot_context *p_kc_new = NULL, *kc = NULL, *kc_new = NULL;
+
+ /* Unbound keyslot (no assigned data segment) is special case */
+ if (ARG_SET(OPT_UNBOUND_ID))
+ return luksAddUnboundKey();
+
+ /* maintain backward compatibility of luksAddKey action positional parameter */
+ if (!new_key_file)
+ new_key_file = ARG_STR(OPT_NEW_KEYFILE_ID);
+
+ keyslot_old = ARG_INT32(OPT_KEY_SLOT_ID);
+ keyslot_new = ARG_INT32(OPT_NEW_KEY_SLOT_ID);
+
+ /*
+ * maintain backward compatibility of --key-slot/-S as 'new keyslot number'
+ * unless --new-key-slot is used.
+ */
+ if (!ARG_SET(OPT_NEW_KEY_SLOT_ID) && ARG_SET(OPT_KEY_SLOT_ID)) {
+ if (!ARG_SET(OPT_BATCH_MODE_ID))
+ log_std(_("WARNING: The --key-slot parameter is used for new keyslot number.\n"));
+ keyslot_old = CRYPT_ANY_SLOT;
+ keyslot_new = ARG_INT32(OPT_KEY_SLOT_ID);
+ }
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ if ((r = crypt_load(cd, luksType(device_type), NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ r = _set_keyslot_encryption_params(cd);
+ if (r < 0)
+ goto out;
+
+ /* Never call pwquality if using null cipher */
+ if (crypt_is_cipher_null(crypt_get_cipher(cd)))
+ ARG_SET_TRUE(OPT_FORCE_PASSWORD_ID);
+
+ keysize = crypt_get_volume_key_size(cd);
+ r = set_pbkdf_params(cd, crypt_get_type(cd));
+ if (r) {
+ log_err(_("Failed to set pbkdf parameters."));
+ goto out;
+ }
+
+ if (ARG_SET(OPT_VOLUME_KEY_FILE_ID)) {
+ if (!keysize && !ARG_SET(OPT_KEY_SIZE_ID)) {
+ log_err(_("Cannot determine volume key size for LUKS without keyslots, please use --key-size option."));
+ r = -EINVAL;
+ goto out;
+ } else if (!keysize)
+ keysize = ARG_UINT32(OPT_KEY_SIZE_ID) / 8;
+
+ r = tools_read_vk(ARG_STR(OPT_VOLUME_KEY_FILE_ID), &key, keysize);
+ if (r < 0)
+ goto out;
+
+ r = crypt_volume_key_verify(cd, key, keysize);
+ if (r == -EPERM)
+ log_err(_("Volume key does not match the volume."));
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+ r = crypt_keyslot_context_init_by_volume_key(cd, key, keysize, &kc);
+ } else if (ARG_SET(OPT_KEY_FILE_ID) && !tools_is_stdin(ARG_STR(OPT_KEY_FILE_ID)))
+ r = crypt_keyslot_context_init_by_keyfile(cd,
+ ARG_STR(OPT_KEY_FILE_ID),
+ ARG_UINT32(OPT_KEYFILE_SIZE_ID),
+ ARG_UINT64(OPT_KEYFILE_OFFSET_ID),
+ &kc);
+ else if (ARG_SET(OPT_TOKEN_ID_ID) || ARG_SET(OPT_TOKEN_TYPE_ID) || ARG_SET(OPT_TOKEN_ONLY_ID)) {
+ r = crypt_keyslot_context_init_by_token(cd,
+ ARG_INT32(OPT_TOKEN_ID_ID),
+ ARG_STR(OPT_TOKEN_TYPE_ID),
+ NULL, 0, NULL, &kc);
+ } else {
+ r = tools_get_key(_("Enter any existing passphrase: "),
+ &password, &password_size,
+ ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID),
+ ARG_UINT32(OPT_TIMEOUT_ID), verify_passphrase(0), 0, cd);
+
+ if (r < 0)
+ goto out;
+
+ /* Check password before asking for new one */
+ r = crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT,
+ password, password_size, 0);
+ check_signal(&r);
+ tools_passphrase_msg(r);
+ if (r < 0)
+ goto out;
+ tools_keyslot_msg(r, UNLOCKED);
+
+ r = crypt_keyslot_context_init_by_passphrase(cd, password, password_size, &kc);
+ }
+
+ if (r < 0)
+ goto out;
+
+ if (new_key_file && !tools_is_stdin(new_key_file)) {
+ if (ARG_SET(OPT_KEY_FILE_ID) && !strcmp(ARG_STR(OPT_KEY_FILE_ID), new_key_file))
+ p_kc_new = kc;
+ else {
+ r = crypt_keyslot_context_init_by_keyfile(cd,
+ new_key_file,
+ ARG_UINT32(OPT_NEW_KEYFILE_SIZE_ID),
+ ARG_UINT64(OPT_NEW_KEYFILE_OFFSET_ID),
+ &kc_new);
+ p_kc_new = kc_new;
+ }
+ } else if (ARG_SET(OPT_NEW_TOKEN_ID_ID)) {
+ if (ARG_INT32(OPT_NEW_TOKEN_ID_ID) == ARG_INT32(OPT_TOKEN_ID_ID))
+ p_kc_new = kc;
+ else {
+ r = crypt_keyslot_context_init_by_token(cd,
+ ARG_INT32(OPT_NEW_TOKEN_ID_ID),
+ NULL, NULL, 0, NULL, &kc_new);
+ p_kc_new = kc_new;
+ }
+ } else {
+ r = tools_get_key(_("Enter new passphrase for key slot: "),
+ &password_new, &password_new_size,
+ ARG_UINT64(OPT_NEW_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_NEW_KEYFILE_SIZE_ID), new_key_file,
+ ARG_UINT32(OPT_TIMEOUT_ID), verify_passphrase(1), !ARG_SET(OPT_FORCE_PASSWORD_ID), cd);
+
+ if (r < 0)
+ goto out;
+ r = crypt_keyslot_context_init_by_passphrase(cd, password_new, password_new_size, &kc_new);
+ }
+
+ if (r < 0)
+ goto out;
+
+ if (!p_kc_new)
+ p_kc_new = kc_new;
+
+ r = try_keyslot_add(cd, keyslot_old, keyslot_new, kc, p_kc_new, pin, pin_new);
+ if (r >= 0 || r != -ENOANO)
+ goto out;
+
+ if (crypt_keyslot_context_get_error(kc) == -ENOANO) {
+ r = _ask_for_pin(cd, ARG_INT32(OPT_TOKEN_ID_ID), &pin, &pin_size, kc);
+ if (r < 0)
+ goto out;
+
+ r = try_keyslot_add(cd, keyslot_old, keyslot_new, kc, p_kc_new, pin, pin_new);
+ if (r >= 0 || r != -ENOANO)
+ goto out;
+ }
+
+ if (crypt_keyslot_context_get_error(p_kc_new) == -ENOANO) {
+ r = _ask_for_pin(cd, ARG_INT32(OPT_NEW_TOKEN_ID_ID), &pin_new, &pin_size_new, p_kc_new);
+ if (r < 0)
+ goto out;
+ r = try_keyslot_add(cd, keyslot_old, keyslot_new, kc, p_kc_new, pin, pin_new);
+ }
+out:
+ tools_keyslot_msg(r, CREATED);
+ crypt_keyslot_context_free(kc);
+ crypt_keyslot_context_free(kc_new);
+ crypt_safe_free(password);
+ crypt_safe_free(password_new);
+ crypt_safe_free(pin);
+ crypt_safe_free(pin_new);
+ crypt_safe_free(key);
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksChangeKey(void)
+{
+ const char *new_key_file = (action_argc > 1 ? action_argv[1] : NULL);
+ struct crypt_device *cd = NULL;
+ 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_header(NULL))))
+ goto out;
+
+ if ((r = crypt_load(cd, luksType(device_type), NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ r = _set_keyslot_encryption_params(cd);
+ if (r < 0)
+ goto out;
+
+ /* Never call pwquality if using null cipher */
+ if (crypt_is_cipher_null(crypt_get_cipher(cd)))
+ ARG_SET_TRUE(OPT_FORCE_PASSWORD_ID);
+
+ r = set_pbkdf_params(cd, crypt_get_type(cd));
+ if (r) {
+ log_err(_("Failed to set pbkdf parameters."));
+ goto out;
+ }
+
+ r = tools_get_key(_("Enter passphrase to be changed: "),
+ &password, &password_size,
+ ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID),
+ ARG_UINT32(OPT_TIMEOUT_ID), verify_passphrase(0), 0, cd);
+ if (r < 0)
+ goto out;
+
+ /* Check password before asking for new one */
+ r = crypt_activate_by_passphrase(cd, NULL, ARG_INT32(OPT_KEY_SLOT_ID),
+ password, password_size, CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY);
+ tools_passphrase_msg(r);
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+ tools_keyslot_msg(r, UNLOCKED);
+
+ r = tools_get_key(_("Enter new passphrase: "),
+ &password_new, &password_new_size,
+ ARG_UINT64(OPT_NEW_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_NEW_KEYFILE_SIZE_ID),
+ new_key_file,
+ ARG_UINT32(OPT_TIMEOUT_ID), verify_passphrase(1), !ARG_SET(OPT_FORCE_PASSWORD_ID), cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_keyslot_change_by_passphrase(cd, ARG_INT32(OPT_KEY_SLOT_ID), ARG_INT32(OPT_KEY_SLOT_ID),
+ password, password_size, password_new, password_new_size);
+ tools_keyslot_msg(r, CREATED);
+out:
+ crypt_safe_free(password);
+ crypt_safe_free(password_new);
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksConvertKey(void)
+{
+ struct crypt_device *cd = NULL;
+ char *password = NULL;
+ size_t password_size = 0;
+ int r;
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ if ((r = crypt_load(cd, CRYPT_LUKS2, NULL))) {
+ log_err(_("Device %s is not a valid LUKS2 device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ r = _set_keyslot_encryption_params(cd);
+ if (r < 0)
+ goto out;
+
+ if (crypt_keyslot_status(cd, ARG_INT32(OPT_KEY_SLOT_ID)) == CRYPT_SLOT_INACTIVE) {
+ r = -EINVAL;
+ log_err(_("Keyslot %d is not active."), ARG_INT32(OPT_KEY_SLOT_ID));
+ goto out;
+ }
+
+ r = set_pbkdf_params(cd, crypt_get_type(cd));
+ if (r) {
+ log_err(_("Failed to set pbkdf parameters."));
+ goto out;
+ }
+
+ r = tools_get_key(_("Enter passphrase for keyslot to be converted: "),
+ &password, &password_size,
+ ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID),
+ ARG_UINT32(OPT_TIMEOUT_ID), verify_passphrase(0), 0, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_keyslot_change_by_passphrase(cd, ARG_INT32(OPT_KEY_SLOT_ID), ARG_INT32(OPT_KEY_SLOT_ID),
+ password, password_size, password, password_size);
+ tools_passphrase_msg(r);
+ tools_keyslot_msg(r, CREATED);
+out:
+ crypt_safe_free(password);
+ crypt_free(cd);
+ return r;
+}
+
+static int action_isLuks(void)
+{
+ struct crypt_device *cd = NULL;
+ int r;
+
+ /* FIXME: argc > max should be checked for other operations as well */
+ if (action_argc > 1) {
+ log_err(_("Only one device argument for isLuks operation is supported."));
+ return -ENODEV;
+ }
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ crypt_set_log_callback(cd, quiet_log, &log_parms);
+ r = crypt_load(cd, luksType(device_type), NULL);
+out:
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksUUID(void)
+{
+ struct crypt_device *cd = NULL;
+ const char *existing_uuid = NULL;
+ int r;
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ if (!ARG_SET(OPT_BATCH_MODE_ID))
+ crypt_set_confirm_callback(cd, yesDialog, _("Operation aborted.\n"));
+
+ if ((r = crypt_load(cd, luksType(device_type), NULL)))
+ goto out;
+
+ if (ARG_SET(OPT_UUID_ID))
+ r = crypt_set_uuid(cd, ARG_STR(OPT_UUID_ID));
+ else {
+ existing_uuid = crypt_get_uuid(cd);
+ log_std("%s\n", existing_uuid ?: "");
+ r = existing_uuid ? 0 : 1;
+ }
+out:
+ crypt_free(cd);
+ return r;
+}
+
+static int luksDump_with_volume_key(struct crypt_device *cd)
+{
+ char *vk = NULL, *password = NULL;
+ size_t passwordLen = 0;
+ size_t vk_size;
+ int r;
+
+ if (!ARG_SET(OPT_BATCH_MODE_ID) && !yesDialog(
+ _("The header dump with volume key is sensitive information\n"
+ "that allows access to encrypted partition without a passphrase.\n"
+ "This dump should be stored encrypted in a safe place."),
+ NULL))
+ return -EPERM;
+
+ vk_size = crypt_get_volume_key_size(cd);
+ vk = crypt_safe_alloc(vk_size);
+ if (!vk)
+ return -ENOMEM;
+
+ r = tools_get_key(NULL, &password, &passwordLen,
+ ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID),
+ ARG_UINT32(OPT_TIMEOUT_ID), 0, 0, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_volume_key_get(cd, CRYPT_ANY_SLOT, vk, &vk_size,
+ password, passwordLen);
+ tools_passphrase_msg(r);
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+ tools_keyslot_msg(r, UNLOCKED);
+
+ if (ARG_SET(OPT_VOLUME_KEY_FILE_ID)) {
+ r = tools_write_mk(ARG_STR(OPT_VOLUME_KEY_FILE_ID), vk, vk_size);
+ if (r < 0)
+ goto out;
+ }
+
+ log_std("LUKS header information for %s\n", crypt_get_device_name(cd));
+ log_std("Cipher name: \t%s\n", crypt_get_cipher(cd));
+ log_std("Cipher mode: \t%s\n", crypt_get_cipher_mode(cd));
+ log_std("Payload offset:\t%d\n", (int)crypt_get_data_offset(cd));
+ log_std("UUID: \t%s\n", crypt_get_uuid(cd));
+ log_std("MK bits: \t%d\n", (int)vk_size * 8);
+ if (ARG_SET(OPT_VOLUME_KEY_FILE_ID)) {
+ log_std("Key stored to file %s.\n", ARG_STR(OPT_VOLUME_KEY_FILE_ID));
+ goto out;
+ }
+ log_std("MK dump:\t");
+ crypt_log_hex(NULL, vk, vk_size, " ", 16, "\n\t\t");
+ log_std("\n");
+out:
+ crypt_safe_free(password);
+ crypt_safe_free(vk);
+ return r;
+}
+
+static int luksDump_with_unbound_key(struct crypt_device *cd)
+{
+ crypt_keyslot_info ki;
+ char *uk = NULL, *password = NULL;
+ size_t uk_size, passwordLen = 0;
+ int r;
+
+ ki = crypt_keyslot_status(cd, ARG_INT32(OPT_KEY_SLOT_ID));
+ if (ki != CRYPT_SLOT_UNBOUND) {
+ log_err(_("Keyslot %d does not contain unbound key."), ARG_INT32(OPT_KEY_SLOT_ID));
+ return -EINVAL;
+ }
+
+ if (!ARG_SET(OPT_BATCH_MODE_ID) && !yesDialog(
+ _("The header dump with unbound key is sensitive information.\n"
+ "This dump should be stored encrypted in a safe place."),
+ NULL))
+ return -EPERM;
+
+ r = crypt_keyslot_get_key_size(cd, ARG_INT32(OPT_KEY_SLOT_ID));
+ if (r < 0)
+ return -EINVAL;
+ uk_size = r;
+ uk = crypt_safe_alloc(uk_size);
+ if (!uk)
+ return -ENOMEM;
+
+ r = tools_get_key(NULL, &password, &passwordLen,
+ ARG_UINT64(OPT_KEYFILE_OFFSET_ID), ARG_UINT32(OPT_KEYFILE_SIZE_ID), ARG_STR(OPT_KEY_FILE_ID),
+ ARG_UINT32(OPT_TIMEOUT_ID), 0, 0, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_volume_key_get(cd, ARG_INT32(OPT_KEY_SLOT_ID), uk, &uk_size,
+ password, passwordLen);
+ tools_passphrase_msg(r);
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+ tools_keyslot_msg(r, UNLOCKED);
+
+ if (ARG_SET(OPT_VOLUME_KEY_FILE_ID)) {
+ r = tools_write_mk(ARG_STR(OPT_VOLUME_KEY_FILE_ID), uk, uk_size);
+ if (r < 0)
+ goto out;
+ }
+
+ log_std("LUKS header information for %s\n", crypt_get_device_name(cd));
+ log_std("UUID: \t%s\n", crypt_get_uuid(cd));
+ log_std("Keyslot: \t%d\n", ARG_INT32(OPT_KEY_SLOT_ID));
+ log_std("Key bits:\t%d\n", (int)uk_size * 8);
+ if (ARG_SET(OPT_VOLUME_KEY_FILE_ID)) {
+ log_std("Key stored to file %s.\n", ARG_STR(OPT_VOLUME_KEY_FILE_ID));
+ goto out;
+ }
+ log_std("Unbound Key:\t");
+ crypt_log_hex(NULL, uk, uk_size, " ", 16, "\n\t\t");
+ log_std("\n");
+out:
+ crypt_safe_free(password);
+ crypt_safe_free(uk);
+ return r;
+}
+
+static int action_luksDump(void)
+{
+ struct crypt_device *cd = NULL;
+ int r;
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ if ((r = crypt_load(cd, luksType(device_type), NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ if (ARG_SET(OPT_DUMP_VOLUME_KEY_ID))
+ r = luksDump_with_volume_key(cd);
+ else if (ARG_SET(OPT_UNBOUND_ID))
+ r = luksDump_with_unbound_key(cd);
+ else if (ARG_SET(OPT_DUMP_JSON_ID))
+ r = crypt_dump_json(cd, NULL, 0);
+ else
+ r = crypt_dump(cd);
+out:
+ crypt_free(cd);
+ return r;