+static 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 = opt_hash ?: DEFAULT_LUKS1_HASH,
+ .data_alignment = opt_align_payload,
+ .data_device = opt_header_device ? action_argv[0] : NULL,
+ };
+ struct crypt_params_luks2 params2 = {
+ .data_alignment = params1.data_alignment,
+ .data_device = params1.data_device,
+ .sector_size = opt_sector_size ?: SECTOR_SIZE,
+ .label = opt_label,
+ .subsystem = opt_subsystem
+ };
+ void *params;
+
+ type = luksType(opt_type);
+ if (!type)
+ type = crypt_get_default_type();
+
+ if (!strcmp(type, CRYPT_LUKS2)) {
+ params = ¶ms2;
+ } else if (!strcmp(type, CRYPT_LUKS1)) {
+ params = ¶ms1;
+
+ if (opt_sector_size > SECTOR_SIZE) {
+ log_err(_("Unsupported encryption sector size."));
+ return -EINVAL;
+ }
+
+ if (opt_integrity) {
+ log_err(_("Integrity option can be used only for LUKS2 format."));
+ return -EINVAL;
+ }
+
+ if (opt_luks2_keyslots_size || opt_luks2_metadata_size) {
+ log_err(_("Unsupported LUKS2 metadata size options."));
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
+
+ /* Create header file (must contain at least one sector)? */
+ if (opt_header_device && stat(opt_header_device, &st) < 0 && errno == ENOENT) {
+ if (!opt_batch_mode &&
+ !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(opt_header_device, 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."), opt_header_device);
+ else {
+ r = 0;
+ created = 1;
+ }
+ if (fd != -1)
+ close(fd);
+ if (r < 0)
+ return r;
+ }
+
+ header_device = opt_header_device ?: action_argv[0];
+
+ r = crypt_parse_name_and_mode(opt_cipher ?: DEFAULT_CIPHER(LUKS1),
+ cipher, NULL, cipher_mode);
+ if (r < 0) {
+ log_err(_("No known cipher specification pattern detected."));
+ goto out;
+ }
+
+ if (opt_integrity) {
+ r = crypt_parse_integrity_mode(opt_integrity, 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 (tools_is_cipher_null(cipher))
+ opt_force_password = 1;
+
+ if ((r = crypt_init(&cd, header_device))) {
+ if (opt_header_device)
+ log_err(_("Cannot use %s as on-disk header."), header_device);
+ return r;
+ }
+
+ if (opt_luks2_keyslots_size || opt_luks2_metadata_size) {
+ r = crypt_set_metadata_size(cd, opt_luks2_metadata_size, opt_luks2_keyslots_size);
+ if (r < 0) {
+ log_err(_("Unsupported LUKS2 metadata size options."));
+ goto out;
+ }
+ }
+
+ if (opt_offset) {
+ r = crypt_set_data_offset(cd, opt_offset);
+ if (r < 0)
+ goto out;
+ }
+
+ /* Print all present signatures in read-only mode */
+ r = tools_detect_signatures(header_device, 0, &signatures);
+ if (r < 0)
+ goto out;
+
+ if (!created) {
+ 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;
+ }
+
+#ifdef ENABLE_LUKS_ADJUST_XTS_KEYSIZE
+ if (!opt_key_size && !strncmp(cipher_mode, "xts-", 4)) {
+ if (DEFAULT_LUKS1_KEYBITS == 128)
+ opt_key_size = 256;
+ else if (DEFAULT_LUKS1_KEYBITS == 256)
+ opt_key_size = 512;
+ }
+#endif
+ keysize = (opt_key_size ?: DEFAULT_LUKS1_KEYBITS) / 8 + integrity_keysize;
+
+ if (opt_random)
+ crypt_set_rng_type(cd, CRYPT_RNG_RANDOM);
+ else if (opt_urandom)
+ crypt_set_rng_type(cd, CRYPT_RNG_URANDOM);
+
+ r = tools_get_key(NULL, &password, &passwordLen,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout, _verify_passphrase(1), 1, cd);
+ if (r < 0)
+ goto out;
+
+ if (opt_master_key_file) {
+ r = tools_read_mk(opt_master_key_file, &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)) < 0))
+ goto out;
+
+ if (opt_integrity_legacy_padding)
+ crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_PADDING);
+
+ r = crypt_format(cd, type, cipher, cipher_mode,
+ opt_uuid, 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, opt_key_slot,
+ key, keysize,
+ password, passwordLen);
+ if (r < 0) {
+ (void) tools_wipe_all_signatures(header_device);
+ goto out;
+ }
+ tools_keyslot_msg(r, CREATED);
+
+ if (opt_integrity && !opt_integrity_no_wipe &&
+ 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;
+
+ if (opt_refresh) {
+ activated_name = action_argc > 1 ? action_argv[1] : action_argv[0];
+ r = crypt_init_by_name_and_header(&cd, activated_name, opt_header_device);
+ if (r)
+ goto out;
+ activate_flags |= CRYPT_ACTIVATE_REFRESH;
+ } else {
+ header_device = uuid_or_device_header(&data_device);
+
+ activated_name = opt_test_passphrase ? NULL : action_argv[1];
+
+ if ((r = crypt_init_data_device(&cd, header_device, data_device)))
+ goto out;
+
+ if ((r = crypt_load(cd, luksType(opt_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) && !opt_test_passphrase) {
+ log_err(_("Reduced data offset is allowed only for detached LUKS header."));
+ r = -EINVAL;
+ goto out;
+ }
+ }
+
+ _set_activation_flags(&activate_flags);
+
+ if (opt_master_key_file) {
+ keysize = crypt_get_volume_key_size(cd);
+ if (!keysize && !opt_key_size) {
+ log_err(_("Cannot determine volume key size for LUKS without keyslots, please use --key-size option."));
+ r = -EINVAL;
+ goto out;
+ } else if (!keysize)
+ keysize = opt_key_size / 8;
+
+ r = tools_read_mk(opt_master_key_file, &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(cd, activated_name, opt_token, NULL, activate_flags);
+ tools_keyslot_msg(r, UNLOCKED);
+ if (r >= 0 || opt_token_only)
+ goto out;
+
+ tries = (tools_is_stdin(opt_key_file) && isatty(STDIN_FILENO)) ? opt_tries : 1;
+ do {
+ r = tools_get_key(NULL, &password, &passwordLen,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout, _verify_passphrase(0), 0, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_activate_by_passphrase(cd, activated_name,
+ opt_key_slot, 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 && opt_persistent &&
+ (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 && !opt_batch_mode && !key_file &&
+ msg_last && !yesDialog(msg_last, msg_fail))
+ return -EPERM;
+
+ r = tools_get_key(msg_pass, &password, &passwordLen,
+ keyfile_offset, keyfile_size, key_file, opt_timeout,
+ _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;
+
+ crypt_set_confirm_callback(cd, yesDialog, NULL);
+
+ if ((r = crypt_load(cd, luksType(opt_type), NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ ki = crypt_keyslot_status(cd, opt_key_slot);
+ switch (ki) {
+ case CRYPT_SLOT_ACTIVE_LAST:
+ case CRYPT_SLOT_ACTIVE:
+ case CRYPT_SLOT_UNBOUND:
+ log_verbose(_("Keyslot %d is selected for deletion."), opt_key_slot);
+ break;
+ case CRYPT_SLOT_INACTIVE:
+ log_err(_("Keyslot %d is not active."), opt_key_slot);
+ /* fall through */
+ case CRYPT_SLOT_INVALID:
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (!opt_batch_mode || opt_key_file || !isatty(STDIN_FILENO)) {
+ r = verify_keyslot(cd, opt_key_slot, 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"),
+ opt_key_file, opt_keyfile_offset, opt_keyfile_size);
+ tools_keyslot_msg(r, UNLOCKED);
+
+ if (r == -EPIPE && (!opt_key_file || tools_is_stdin(opt_key_file))) {
+ log_dbg("Failed read from input, ignoring passphrase.");
+ r = 0;
+ }
+
+ if (r < 0)
+ goto out;
+ }
+
+ r = crypt_keyslot_destroy(cd, opt_key_slot);
+ tools_keyslot_msg(opt_key_slot, 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;
+
+ crypt_set_confirm_callback(cd, yesDialog, NULL);
+
+ if ((r = crypt_load(cd, luksType(opt_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,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout,
+ _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);
+
+ opt_key_slot = r;
+ log_verbose(_("Keyslot %d is selected for deletion."), 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."),
+ _("Operation aborted, the keyslot was NOT wiped.\n"))) {
+ r = -EPERM;
+ goto out;
+ }
+
+ r = crypt_keyslot_destroy(cd, opt_key_slot);
+ tools_keyslot_msg(opt_key_slot, 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 *opt_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;
+
+ crypt_set_confirm_callback(cd, yesDialog, NULL);
+
+ if ((r = crypt_load(cd, CRYPT_LUKS2, 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 (tools_is_cipher_null(crypt_get_cipher(cd)))
+ opt_force_password = 1;
+
+ keysize = opt_key_size / 8;
+ r = set_pbkdf_params(cd, crypt_get_type(cd));
+ if (r) {
+ log_err(_("Failed to set pbkdf parameters."));
+ goto out;
+ }
+
+ if (opt_master_key_file) {
+ r = tools_read_mk(opt_master_key_file, &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,
+ opt_new_keyfile_offset, opt_new_keyfile_size,
+ opt_new_key_file, opt_timeout,
+ _verify_passphrase(1), 1, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_keyslot_add_by_key(cd, opt_key_slot, 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 action_luksAddKey(void)
+{
+ int r = -EINVAL, keysize = 0;
+ char *key = NULL;
+ const char *opt_new_key_file = (action_argc > 1 ? action_argv[1] : NULL);
+ char *password = NULL, *password_new = NULL;
+ size_t password_size = 0, password_new_size = 0;
+ struct crypt_device *cd = NULL;
+
+ /* Unbound keyslot (no assigned data segment) is special case */
+ if (opt_unbound)
+ return luksAddUnboundKey();
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ crypt_set_confirm_callback(cd, yesDialog, NULL);
+
+ if ((r = crypt_load(cd, luksType(opt_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 (tools_is_cipher_null(crypt_get_cipher(cd)))
+ opt_force_password = 1;
+
+ 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 (opt_master_key_file) {
+ if (!keysize && !opt_key_size) {
+ log_err(_("Cannot determine volume key size for LUKS without keyslots, please use --key-size option."));
+ r = -EINVAL;
+ goto out;
+ } else if (!keysize)
+ keysize = opt_key_size / 8;
+
+ r = tools_read_mk(opt_master_key_file, &key, keysize);
+ if (r < 0)
+ goto out;
+
+ r = crypt_volume_key_verify(cd, key, keysize);
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+
+ r = tools_get_key(_("Enter new passphrase for key slot: "),
+ &password_new, &password_new_size,
+ opt_new_keyfile_offset, opt_new_keyfile_size,
+ opt_new_key_file, opt_timeout,
+ _verify_passphrase(1), 1, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_keyslot_add_by_volume_key(cd, opt_key_slot, key, keysize,
+ password_new, password_new_size);
+ } else if (opt_key_file && !tools_is_stdin(opt_key_file) &&
+ opt_new_key_file && !tools_is_stdin(opt_new_key_file)) {
+ r = crypt_keyslot_add_by_keyfile_device_offset(cd, opt_key_slot,
+ opt_key_file, opt_keyfile_size, opt_keyfile_offset,
+ opt_new_key_file, opt_new_keyfile_size, opt_new_keyfile_offset);
+ tools_passphrase_msg(r);
+ } else {
+ r = tools_get_key(_("Enter any existing passphrase: "),
+ &password, &password_size,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout, _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 = tools_get_key(_("Enter new passphrase for key slot: "),
+ &password_new, &password_new_size,
+ opt_new_keyfile_offset, opt_new_keyfile_size, opt_new_key_file,
+ opt_timeout, _verify_passphrase(1), 1, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_keyslot_add_by_passphrase(cd, opt_key_slot,
+ password, password_size,
+ password_new, password_new_size);
+ }
+out:
+ tools_keyslot_msg(r, CREATED);
+ crypt_safe_free(password);
+ crypt_safe_free(password_new);
+ crypt_safe_free(key);
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksChangeKey(void)
+{
+ const char *opt_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(opt_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 (tools_is_cipher_null(crypt_get_cipher(cd)))
+ opt_force_password = 1;
+
+ 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,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout, _verify_passphrase(0), 0, cd);
+ if (r < 0)
+ goto out;
+
+ /* Check password before asking for new one */
+ r = crypt_activate_by_passphrase(cd, NULL, opt_key_slot,
+ 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,
+ opt_new_keyfile_offset, opt_new_keyfile_size,
+ opt_new_key_file,
+ opt_timeout, _verify_passphrase(1), 1, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_keyslot_change_by_passphrase(cd, opt_key_slot, opt_key_slot,
+ 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 LUKS device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ r = _set_keyslot_encryption_params(cd);
+ if (r < 0)
+ goto out;
+
+ if (crypt_keyslot_status(cd, opt_key_slot) == CRYPT_SLOT_INACTIVE) {
+ r = -EINVAL;
+ log_err(_("Keyslot %d is not active."), opt_key_slot);
+ 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,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout, _verify_passphrase(0), 0, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_keyslot_change_by_passphrase(cd, opt_key_slot, opt_key_slot,
+ 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, NULL);
+ r = crypt_load(cd, luksType(opt_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;
+
+ crypt_set_confirm_callback(cd, yesDialog, _("Operation aborted.\n"));
+
+ if ((r = crypt_load(cd, luksType(opt_type), NULL)))
+ goto out;
+
+ if (opt_uuid)
+ r = crypt_set_uuid(cd, opt_uuid);
+ 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;
+ unsigned i;
+ int r;
+
+ crypt_set_confirm_callback(cd, yesDialog, NULL);
+ if (!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,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout, 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 (opt_master_key_file) {
+ r = tools_write_mk(opt_master_key_file, 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 (opt_master_key_file) {
+ log_std("Key stored to file %s.\n", opt_master_key_file);
+ goto out;
+ }
+ log_std("MK dump:\t");
+
+ for(i = 0; i < vk_size; i++) {
+ if (i && !(i % 16))
+ log_std("\n\t\t");
+ log_std("%02hhx ", (char)vk[i]);
+ }
+ 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 i, r;
+
+ ki = crypt_keyslot_status(cd, opt_key_slot);
+ if (ki != CRYPT_SLOT_UNBOUND) {
+ log_err(_("Keyslot %d does not contain unbound key."), opt_key_slot);
+ return -EINVAL;
+ }
+
+ crypt_set_confirm_callback(cd, yesDialog, NULL);
+ if (!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, opt_key_slot);
+ 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,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout, 0, 0, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_volume_key_get(cd, opt_key_slot, uk, &uk_size,
+ password, passwordLen);
+ tools_passphrase_msg(r);
+ check_signal(&r);
+ if (r < 0)
+ goto out;
+ tools_keyslot_msg(r, UNLOCKED);
+
+ if (opt_master_key_file) {
+ r = tools_write_mk(opt_master_key_file, 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", opt_key_slot);
+ log_std("Key bits:\t%d\n", (int)uk_size * 8);
+ if (opt_master_key_file) {
+ log_std("Key stored to file %s.\n", opt_master_key_file);
+ goto out;
+ }
+ log_std("Unbound Key:\t");
+
+ for(i = 0; i < (int)uk_size; i++) {
+ if (i && !(i % 16))
+ log_std("\n\t\t");
+ log_std("%02hhx ", (char)uk[i]);
+ }
+ 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(opt_type), NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ if (opt_dump_master_key)
+ r = luksDump_with_volume_key(cd);
+ else if (opt_unbound)
+ r = luksDump_with_unbound_key(cd);
+ else
+ r = crypt_dump(cd);
+out:
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksSuspend(void)
+{
+ struct crypt_device *cd = NULL;
+ int r;
+
+ r = crypt_init_by_name_and_header(&cd, action_argv[0], uuid_or_device(opt_header_device));
+ if (!r)
+ r = crypt_suspend(cd, action_argv[0]);
+
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksResume(void)
+{
+ struct crypt_device *cd = NULL;
+ char *password = NULL;
+ size_t passwordLen;
+ int r, tries;
+
+ if ((r = crypt_init_by_name_and_header(&cd, action_argv[0], uuid_or_device(opt_header_device))))
+ goto out;
+
+ if ((r = crypt_load(cd, luksType(opt_type), NULL)))
+ goto out;
+
+ tries = (tools_is_stdin(opt_key_file) && isatty(STDIN_FILENO)) ? opt_tries : 1;
+ do {
+ r = tools_get_key(NULL, &password, &passwordLen,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout, _verify_passphrase(0), 0, cd);
+ if (r < 0)
+ goto out;
+
+ r = crypt_resume_by_passphrase(cd, action_argv[0], CRYPT_ANY_SLOT,
+ password, passwordLen);
+ tools_passphrase_msg(r);
+ check_signal(&r);
+ tools_keyslot_msg(r, UNLOCKED);
+
+ crypt_safe_free(password);
+ password = NULL;
+ } while ((r == -EPERM || r == -ERANGE) && (--tries > 0));
+out:
+ crypt_safe_free(password);
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksBackup(void)
+{
+ struct crypt_device *cd = NULL;
+ int r;
+
+ if (!opt_header_backup_file) {
+ log_err(_("Option --header-backup-file is required."));
+ return -EINVAL;
+ }
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ crypt_set_confirm_callback(cd, yesDialog, NULL);
+
+ r = crypt_header_backup(cd, NULL, opt_header_backup_file);
+out:
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksRestore(void)
+{
+ struct crypt_device *cd = NULL;
+ int r = 0;
+
+ if (!opt_header_backup_file) {
+ log_err(_("Option --header-backup-file is required."));
+ return -EINVAL;
+ }
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ crypt_set_confirm_callback(cd, yesDialog, NULL);
+ r = crypt_header_restore(cd, NULL, opt_header_backup_file);
+out:
+ crypt_free(cd);
+ return r;
+}
+
+static const char *_get_device_type(void)
+{
+ const char *type, *name = NULL;
+ struct crypt_device *cd = NULL;
+
+ if (action_argc > 1)
+ name = action_argv[1];
+ else if (action_argc == 1)
+ name = action_argv[0];
+
+ if (crypt_init_by_name_and_header(&cd, name, opt_header_device))
+ return NULL;
+
+ type = crypt_get_type(cd);
+ if (!type) {
+ crypt_free(cd);
+ log_err(_("%s is not cryptsetup managed device."), name);
+ return NULL;
+ }
+
+ if (!strncmp(type, "LUKS", 4))
+ type = "luks";
+ else if (!strcmp(type, CRYPT_PLAIN))
+ type = "plain";
+ else if (!strcmp(type, CRYPT_LOOPAES))
+ type = "loopaes";
+ else {
+ log_err(_("Refresh is not supported for device type %s"), type);
+ type = NULL;
+ }
+
+ crypt_free(cd);
+
+ return type;
+}
+
+static int action_open(void)
+{
+ if (opt_refresh && !opt_type)
+ /* read device type from active mapping */
+ opt_type = _get_device_type();
+
+ if (!opt_type)
+ return -EINVAL;
+
+ if (!strcmp(opt_type, "luks") ||
+ !strcmp(opt_type, "luks1") ||
+ !strcmp(opt_type, "luks2")) {
+ if (action_argc < 2 && (!opt_test_passphrase && !opt_refresh))
+ goto args;
+ return action_open_luks();
+ } else if (!strcmp(opt_type, "plain")) {
+ if (action_argc < 2 && !opt_refresh)
+ goto args;
+ return action_open_plain();
+ } else if (!strcmp(opt_type, "loopaes")) {
+ if (action_argc < 2 && !opt_refresh)
+ goto args;
+ return action_open_loopaes();
+ } else if (!strcmp(opt_type, "tcrypt")) {
+ if (action_argc < 2 && !opt_test_passphrase)
+ goto args;
+ return action_open_tcrypt();
+ } else if (!strcmp(opt_type, "bitlk")) {
+ if (action_argc < 2 && !opt_test_passphrase)
+ goto args;
+ return action_open_bitlk();
+ }
+
+ log_err(_("Unrecognized metadata device type %s."), opt_type);
+ return -EINVAL;
+args:
+ log_err(_("Command requires device and mapped name as arguments."));
+ return -EINVAL;
+}
+
+static int action_luksErase(void)
+{
+ struct crypt_device *cd = NULL;
+ crypt_keyslot_info ki;
+ char *msg = NULL;
+ int i, max, r;
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ goto out;
+
+ crypt_set_confirm_callback(cd, yesDialog, NULL);
+
+ if ((r = crypt_load(cd, luksType(opt_type), NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ if(asprintf(&msg, _("This operation will erase all keyslots on device %s.\n"
+ "Device will become unusable after this operation."),
+ uuid_or_device_header(NULL)) == -1) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ if (!yesDialog(msg, _("Operation aborted, keyslots were NOT wiped.\n"))) {
+ r = -EPERM;
+ goto out;
+ }
+
+ /* Safety check */
+ max = crypt_keyslot_max(crypt_get_type(cd));
+ if (max <= 0)
+ return -EINVAL;
+
+ for (i = 0; i < max; i++) {
+ ki = crypt_keyslot_status(cd, i);
+ if (ki == CRYPT_SLOT_ACTIVE || ki == CRYPT_SLOT_ACTIVE_LAST) {
+ r = crypt_keyslot_destroy(cd, i);
+ if (r < 0)
+ goto out;
+ tools_keyslot_msg(i, REMOVED);
+ }
+ }
+out:
+ free(msg);
+ crypt_free(cd);
+ return r;
+}
+
+static int action_luksConvert(void)
+{
+ struct crypt_device *cd = NULL;
+ char *msg = NULL;
+ const char *to_type, *from_type;
+ int r;
+
+ if (!strcmp(opt_type, "luks2")) {
+ to_type = CRYPT_LUKS2;
+ } else if (!strcmp(opt_type, "luks1")) {
+ to_type = CRYPT_LUKS1;
+ } else {
+ log_err(_("Invalid LUKS type, only luks1 and luks2 are supported."));
+ return -EINVAL;
+ }
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ return r;
+
+ crypt_set_confirm_callback(cd, yesDialog, NULL);
+
+ if ((r = crypt_load(cd, CRYPT_LUKS, NULL)) ||
+ !(from_type = crypt_get_type(cd))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device_header(NULL));
+ crypt_free(cd);
+ return r;
+ }
+
+ if (!strcmp(from_type, to_type)) {
+ log_err(_("Device is already %s type."), to_type);
+ crypt_free(cd);
+ return -EINVAL;
+ }
+
+ if (asprintf(&msg, _("This operation will convert %s to %s format.\n"),
+ uuid_or_device_header(NULL), to_type) == -1) {
+ crypt_free(cd);
+ return -ENOMEM;
+ }
+
+ if (yesDialog(msg, _("Operation aborted, device was NOT converted.\n")))
+ r = crypt_convert(cd, to_type, NULL);
+ else
+ r = -EPERM;
+
+ free(msg);
+ crypt_free(cd);
+ return r;
+}
+
+static int _config_priority(struct crypt_device *cd)
+{
+ crypt_keyslot_info cs;
+ crypt_keyslot_priority priority = CRYPT_SLOT_PRIORITY_INVALID;
+
+ if (!strcmp("normal", opt_priority))
+ priority = CRYPT_SLOT_PRIORITY_NORMAL;
+ else if (!strcmp("prefer", opt_priority))
+ priority = CRYPT_SLOT_PRIORITY_PREFER;
+ else if (!strcmp("ignore", opt_priority))
+ priority = CRYPT_SLOT_PRIORITY_IGNORE;
+
+ cs = crypt_keyslot_status(cd, opt_key_slot);
+ if (cs != CRYPT_SLOT_INVALID)
+ return crypt_keyslot_set_priority(cd, opt_key_slot, priority);
+
+ return -EINVAL;
+}
+
+static int _config_labels(struct crypt_device *cd)
+{
+ return crypt_set_label(cd, opt_label, opt_subsystem);
+}
+
+static int action_luksConfig(void)
+{
+ struct crypt_device *cd = NULL;
+ int r;
+
+ if (!opt_priority && !opt_label && !opt_subsystem) {
+ log_err(_("Option --priority, --label or --subsystem is missing."));
+ return -EINVAL;
+ }
+
+ if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
+ return r;
+
+ if ((r = crypt_load(cd, CRYPT_LUKS2, NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device_header(NULL));
+ goto out;
+ }
+
+ if (opt_priority && (r = _config_priority(cd)))
+ goto out;
+
+ if ((opt_label || opt_subsystem) && (r = _config_labels(cd)))
+ goto out;
+out:
+ crypt_free(cd);
+ return r;
+}
+
+static int _token_add(struct crypt_device *cd)
+{
+ int r, token;
+ crypt_token_info token_info;
+ const struct crypt_token_params_luks2_keyring params = {
+ .key_description = opt_key_description
+ };
+
+ if (opt_token != CRYPT_ANY_TOKEN) {
+ token_info = crypt_token_status(cd, opt_token, NULL);
+ if (token_info < CRYPT_TOKEN_INACTIVE) {
+ log_err(_("Token %d is invalid."), opt_token);
+ return -EINVAL;
+ } else if (token_info > CRYPT_TOKEN_INACTIVE) {
+ log_err(_("Token %d in use."), opt_token);
+ return -EINVAL;
+ }
+ }
+
+ r = crypt_token_luks2_keyring_set(cd, opt_token, ¶ms);
+ if (r < 0) {
+ log_err(_("Failed to add luks2-keyring token %d."), opt_token);
+ return r;
+ }
+
+ token = r;
+ tools_token_msg(token, CREATED);
+
+ r = crypt_token_assign_keyslot(cd, token, opt_key_slot);
+ if (r < 0) {
+ log_err(_("Failed to assign token %d to keyslot %d."), token, opt_key_slot);
+ (void) crypt_token_json_set(cd, token, NULL);
+ }
+
+ return r;
+}
+
+static int _token_remove(struct crypt_device *cd)
+{
+ crypt_token_info token_info;
+ int r;
+
+ token_info = crypt_token_status(cd, opt_token, NULL);
+ if (token_info < CRYPT_TOKEN_INACTIVE) {
+ log_err(_("Token %d is invalid."), opt_token);
+ return -EINVAL;
+ } else if (token_info == CRYPT_TOKEN_INACTIVE) {
+ log_err(_("Token %d is not in use."), opt_token);
+ return -EINVAL;
+ }
+
+ r = crypt_token_json_set(cd, opt_token, NULL);
+ tools_token_msg(r, REMOVED);
+
+ return r;
+}
+
+static int _token_import(struct crypt_device *cd)
+{
+ char *json;
+ size_t json_length;
+ crypt_token_info token_info;
+ int r, token;
+
+ if (opt_token != CRYPT_ANY_TOKEN) {
+ token_info = crypt_token_status(cd, opt_token, NULL);
+ if (token_info < CRYPT_TOKEN_INACTIVE) {
+ log_err(_("Token %d is invalid."), opt_token);
+ return -EINVAL;
+ } else if (token_info > CRYPT_TOKEN_INACTIVE) {
+ log_err(_("Token %d in use."), opt_token);
+ return -EINVAL;
+ }
+ }
+
+ r = tools_read_json_file(cd, opt_json_file, &json, &json_length);
+ if (r)
+ return r;
+
+ r = crypt_token_json_set(cd, opt_token, json);
+ free(json);
+ if (r < 0) {
+ log_err(_("Failed to import token from file."));
+ return r;
+ }
+
+ token = r;
+ tools_token_msg(token, CREATED);
+
+ if (opt_key_slot != CRYPT_ANY_SLOT) {
+ r = crypt_token_assign_keyslot(cd, token, opt_key_slot);
+ if (r < 0) {
+ log_err(_("Failed to assign token %d to keyslot %d."), token, opt_key_slot);
+ (void) crypt_token_json_set(cd, token, NULL);
+ }
+ }
+
+ return r;
+}
+
+static int _token_export(struct crypt_device *cd)
+{
+ const char *json;
+ int r;
+
+ r = crypt_token_json_get(cd, opt_token, &json);
+ if (r < 0) {
+ log_err(_("Failed to get token %d for export."), opt_token);
+ return r;
+ }
+
+ return tools_write_json_file(cd, opt_json_file, json);
+}
+
+static int action_token(void)
+{
+ int r;
+ struct crypt_device *cd = NULL;
+ enum { ADD = 0, REMOVE, IMPORT, EXPORT } action;
+
+ if (!strcmp(action_argv[0], "add")) {
+ if (!opt_key_description) {
+ log_err(_("--key-description parameter is mandatory for token add action."));
+ return -EINVAL;
+ }
+ action = ADD;
+ } else if (!strcmp(action_argv[0], "remove")) {
+ if (opt_token == CRYPT_ANY_TOKEN) {
+ log_err(_("Action requires specific token. Use --token-id parameter."));
+ return -EINVAL;
+ }
+ action = REMOVE;
+ } else if (!strcmp(action_argv[0], "import")) {
+ action = IMPORT;
+ } else if (!strcmp(action_argv[0], "export")) {
+ if (opt_token == CRYPT_ANY_TOKEN) {
+ log_err(_("Action requires specific token. Use --token-id parameter."));
+ return -EINVAL;
+ }
+ action = EXPORT;
+ } else {
+ log_err(_("Invalid token operation %s."), action_argv[0]);
+ return -EINVAL;
+ }
+
+ if ((r = crypt_init(&cd, uuid_or_device(opt_header_device ?: action_argv[1]))))
+ return r;
+
+ if ((r = crypt_load(cd, CRYPT_LUKS2, NULL))) {
+ log_err(_("Device %s is not a valid LUKS device."),
+ uuid_or_device(opt_header_device ?: action_argv[1]));
+ crypt_free(cd);
+ return r;
+ }
+
+ if (action == ADD)
+ r = _token_add(cd); /* adds only luks2-keyring type */
+ else if (action == REMOVE)
+ r = _token_remove(cd);
+ else if (action == IMPORT)
+ r = _token_import(cd);
+ else if (action == EXPORT)
+ r = _token_export(cd);
+ else {
+ log_dbg("Internal token action error.");
+ r = -EINVAL;
+ }
+
+ crypt_free(cd);
+
+ return r;
+}
+
+static int auto_detect_active_name(struct crypt_device *cd, const char *data_device, char *dm_name, size_t dm_name_len)
+{
+ int r;
+
+ r = tools_lookup_crypt_device(cd, crypt_get_type(cd), data_device, dm_name, dm_name_len);
+ if (r > 0)
+ log_dbg("Device %s has %d active holders.", data_device, r);
+
+ return r;
+}
+
+static int _get_device_active_name(struct crypt_device *cd, const char *data_device, char *buffer, size_t buffer_size)
+{
+ char *msg;
+ int r;
+
+ r = auto_detect_active_name(cd, action_argv[0], buffer, buffer_size);
+ if (r > 0) {
+ if (*buffer == '\0') {
+ log_err(_("Device %s is still in use."), data_device);
+ return -EINVAL;
+ }
+ if (!opt_batch_mode)
+ log_std(_("Auto-detected active dm device '%s' for data device %s.\n"), buffer, data_device);
+ }
+ if (r < 0) {
+ if (r == -ENOTBLK)
+ log_std(_("Device %s is not a block device.\n"), data_device);
+ else
+ log_err(_("Failed to auto-detect device %s holders."), data_device);
+
+ r = asprintf(&msg, _("Unable to decide if device %s is activated or not.\n"
+ "Are you sure you want to proceed with reencryption in offline mode?\n"
+ "It may lead to data corruption if the device is actually activated.\n"
+ "To run reencryption in online mode, use --active-name parameter instead.\n"), data_device);
+ if (r < 0)
+ return -ENOMEM;
+ r = noDialog(msg, _("Operation aborted.\n")) ? 0 : -EINVAL;
+ free(msg);
+ }
+
+ return r;
+}
+
+static int action_reencrypt_load(struct crypt_device *cd)
+{
+ int r;
+ size_t passwordLen;
+ char dm_name[PATH_MAX] = {}, *password = NULL;
+ const char *active_name = NULL;
+ struct crypt_params_reencrypt params = {
+ .resilience = opt_resilience_mode,
+ .hash = opt_resilience_hash,
+ .max_hotzone_size = opt_hotzone_size / SECTOR_SIZE,
+ .device_size = opt_device_size / SECTOR_SIZE,
+ .flags = CRYPT_REENCRYPT_RESUME_ONLY
+ };
+
+ r = tools_get_key(NULL, &password, &passwordLen,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout, _verify_passphrase(0), 0, cd);
+ if (r < 0)
+ return r;
+
+ if (!opt_active_name) {
+ r = _get_device_active_name(cd, action_argv[0], dm_name, sizeof(dm_name));
+ if (r > 0)
+ active_name = dm_name;
+ if (r < 0) {
+ crypt_safe_free(password);
+ return -EINVAL;
+ }
+ } else
+ active_name = opt_active_name;
+
+ r = crypt_reencrypt_init_by_passphrase(cd, active_name, password, passwordLen, opt_key_slot, opt_key_slot, NULL, NULL, ¶ms);
+
+ crypt_safe_free(password);
+
+ return r;
+}
+
+static int action_encrypt_luks2(struct crypt_device **cd)
+{
+ const char *type, *activated_name = NULL;
+ int keyslot, r, fd;
+ uuid_t uuid;
+ size_t passwordLen;
+ char *msg, uuid_str[37], header_file[PATH_MAX] = { 0 }, *password = NULL;
+ uint32_t activate_flags = 0;
+ const struct crypt_params_luks2 luks2_params = {
+ .sector_size = opt_sector_size ?: SECTOR_SIZE
+ };
+ struct crypt_params_reencrypt params = {
+ .mode = CRYPT_REENCRYPT_ENCRYPT,
+ .direction = opt_data_shift < 0 ? CRYPT_REENCRYPT_BACKWARD : CRYPT_REENCRYPT_FORWARD,
+ .resilience = opt_resilience_mode,
+ .hash = opt_resilience_hash,
+ .max_hotzone_size = opt_hotzone_size / SECTOR_SIZE,
+ .device_size = opt_device_size / SECTOR_SIZE,
+ .luks2 = &luks2_params,
+ .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY
+ };
+
+ _set_reencryption_flags(¶ms.flags);
+
+ type = luksType(opt_type);
+ if (!type)
+ type = crypt_get_default_type();
+
+ if (strcmp(type, CRYPT_LUKS2)) {
+ log_err(_("Invalid LUKS device type."));
+ return -EINVAL;
+ }
+
+ if (!opt_data_shift && !opt_header_device) {
+ log_err(_("Encryption without detached header (--header) is not possible without data device size reduction (--reduce-device-size)."));
+ return -ENOTSUP;
+ }
+
+ if (!opt_header_device && opt_offset && opt_data_shift && (opt_offset > (imaxabs(opt_data_shift) / (2 * SECTOR_SIZE)))) {
+ log_err(_("Requested data offset must be less than or equal to half of --reduce-device-size parameter."));
+ return -EINVAL;
+ }
+
+ /* TODO: ask user to confirm. It's useless to do data device reduction and than use smaller value */
+ if (!opt_header_device && opt_offset && opt_data_shift && (opt_offset < (imaxabs(opt_data_shift) / (2 * SECTOR_SIZE)))) {
+ opt_data_shift = -(opt_offset * 2 * SECTOR_SIZE);
+ if (opt_data_shift >= 0)
+ return -EINVAL;
+ log_std(_("Adjusting --reduce-device-size value to twice the --offset %" PRIu64 " (sectors).\n"), opt_offset * 2);
+ }
+
+ if (strncmp(type, CRYPT_LUKS2, strlen(CRYPT_LUKS2))) {
+ log_err(_("Encryption is supported only for LUKS2 format."));
+ return -EINVAL;
+ }
+
+ if (opt_uuid && uuid_parse(opt_uuid, uuid) == -1) {
+ log_err(_("Wrong LUKS UUID format provided."));
+ return -EINVAL;
+ }
+
+ if (!opt_uuid) {
+ uuid_generate(uuid);
+ uuid_unparse(uuid, uuid_str);
+ opt_uuid = uuid_str;
+ }
+
+ /* Check the data device is not LUKS device already */
+ if ((r = crypt_init(cd, action_argv[0])))
+ return r;
+ r = crypt_load(*cd, CRYPT_LUKS, NULL);
+ crypt_free(*cd);
+ *cd = NULL;
+ if (!r) {
+ r = asprintf(&msg, _("Detected LUKS device on %s. Do you want to encrypt that LUKS device again?"), action_argv[0]);
+ if (r == -1)
+ return -ENOMEM;
+
+ r = yesDialog(msg, _("Operation aborted.\n")) ? 0 : -EINVAL;
+ free(msg);
+ if (r < 0)
+ return r;
+ }
+
+ if (!opt_header_device) {
+ snprintf(header_file, sizeof(header_file), "LUKS2-temp-%s.new", opt_uuid);
+ fd = open(header_file, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (fd == -1) {
+ if (errno == EEXIST)
+ log_err(_("Temporary header file %s already exists. Aborting."), header_file);
+ else
+ log_err(_("Cannot create temporary header file %s."), header_file);
+ return -EINVAL;
+ }
+
+ r = posix_fallocate(fd, 0, 4096);
+ close(fd);
+ if (r) {
+ log_err(_("Cannot create temporary header file %s."), header_file);
+ r = -EINVAL;
+ goto err;
+ }
+
+ opt_header_device = header_file;
+ /*
+ * FIXME: just override offset here, but we should support both.
+ * offset and implicit offset via data shift (lvprepend?)
+ */
+ if (!opt_offset)
+ opt_offset = imaxabs(opt_data_shift) / (2 * SECTOR_SIZE);
+ opt_data_shift >>= 1;
+ params.flags |= CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT;
+ } else if (opt_data_shift < 0) {
+ if (!opt_luks2_metadata_size)
+ opt_luks2_metadata_size = 0x4000; /* missing default here */
+ if (!opt_luks2_keyslots_size)
+ opt_luks2_keyslots_size = -opt_data_shift - 2 * opt_luks2_metadata_size;
+
+ if (2 * opt_luks2_metadata_size + opt_luks2_keyslots_size > (uint64_t)-opt_data_shift) {
+ log_err("LUKS2 metadata size is larger than data shift value.");
+ return -EINVAL;
+ }
+ }
+
+ r = _luksFormat(cd, &password, &passwordLen);
+ if (r < 0)
+ goto err;
+
+ if (opt_data_shift) {
+ params.data_shift = imaxabs(opt_data_shift) / SECTOR_SIZE,
+ params.resilience = "datashift";
+ }
+ keyslot = opt_key_slot < 0 ? 0 : opt_key_slot;
+ r = crypt_reencrypt_init_by_passphrase(*cd, NULL, password, passwordLen,
+ CRYPT_ANY_SLOT, keyslot, crypt_get_cipher(*cd),
+ crypt_get_cipher_mode(*cd), ¶ms);
+ if (r < 0) {
+ crypt_keyslot_destroy(*cd, keyslot);
+ goto err;
+ }
+
+ /* Restore temporary header in head of data device */
+ if (*header_file) {
+ crypt_free(*cd);
+ *cd = NULL;
+
+ r = crypt_init(cd, action_argv[0]);
+ if (!r)
+ r = crypt_header_restore(*cd, CRYPT_LUKS2, header_file);
+
+ if (r) {
+ log_err("Failed to place new header at head of device %s.", action_argv[0]);
+ goto err;
+ }
+ }
+
+ /* activate device */
+ if (action_argc > 1) {
+ activated_name = action_argv[1];
+ _set_activation_flags(&activate_flags);
+ r = crypt_activate_by_passphrase(*cd, activated_name, opt_key_slot, password, passwordLen, activate_flags);
+ if (r >= 0)
+ log_std(_("%s/%s is now active and ready for online encryption.\n"), crypt_get_dir(), activated_name);
+ }
+
+ if (r < 0)
+ goto err;
+
+ /* just load reencryption context to continue reencryption */
+ if (!opt_reencrypt_init_only) {
+ params.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
+ r = crypt_reencrypt_init_by_passphrase(*cd, activated_name, password, passwordLen,
+ CRYPT_ANY_SLOT, keyslot, NULL, NULL, ¶ms);
+ }
+err:
+ crypt_safe_free(password);
+ if (*header_file)
+ unlink(header_file);
+ return r;
+}
+
+static int action_decrypt_luks2(struct crypt_device *cd)
+{
+ int r;
+ char dm_name[PATH_MAX], *password = NULL;
+ const char *active_name = NULL;
+ struct crypt_params_reencrypt params = {
+ .mode = CRYPT_REENCRYPT_DECRYPT,
+ .direction = opt_data_shift > 0 ? CRYPT_REENCRYPT_FORWARD : CRYPT_REENCRYPT_BACKWARD,
+ .resilience = opt_data_shift ? "datashift" : opt_resilience_mode,
+ .hash = opt_resilience_hash,
+ .data_shift = imaxabs(opt_data_shift) / SECTOR_SIZE,
+ .device_size = opt_device_size / SECTOR_SIZE,
+ .max_hotzone_size = opt_hotzone_size / SECTOR_SIZE,
+ };
+ size_t passwordLen;
+
+ _set_reencryption_flags(¶ms.flags);
+
+ r = tools_get_key(NULL, &password, &passwordLen,
+ opt_keyfile_offset, opt_keyfile_size, opt_key_file,
+ opt_timeout, _verify_passphrase(0), 0, cd);
+ if (r < 0)
+ return r;
+
+ if (!opt_active_name) {
+ r = _get_device_active_name(cd, action_argv[0], dm_name, sizeof(dm_name));
+ if (r > 0)
+ active_name = dm_name;
+ if (r < 0)
+ goto err;
+ } else
+ active_name = opt_active_name;
+
+ if (!active_name)
+ log_dbg("Device %s seems unused. Proceeding with offline operation.", action_argv[0]);
+
+ r = crypt_reencrypt_init_by_passphrase(cd, active_name, password,
+ passwordLen, opt_key_slot, CRYPT_ANY_SLOT, NULL, NULL, ¶ms);
+err:
+ crypt_safe_free(password);
+ return r;
+}
+
+struct keyslot_passwords {
+ char *password;
+ size_t passwordLen;
+ int new;
+};
+
+static struct keyslot_passwords *init_keyslot_passwords(size_t count)
+{
+ size_t i;
+ struct keyslot_passwords *tmp = calloc(count, sizeof(struct keyslot_passwords));
+
+ if (!tmp)
+ return tmp;
+
+ for (i = 0; i < count; i++)
+ tmp[i].new = -1;
+
+ return tmp;
+}
+
+static int init_passphrase(struct keyslot_passwords *kp, size_t keyslot_passwords_length,
+ struct crypt_device *cd, const char *msg, int slot_to_check)
+{
+ crypt_keyslot_info ki;
+ char *password;
+ int r = -EINVAL, retry_count;
+ size_t passwordLen;
+
+ if (slot_to_check != CRYPT_ANY_SLOT) {
+ ki = crypt_keyslot_status(cd, slot_to_check);
+ if (ki < CRYPT_SLOT_ACTIVE || ki == CRYPT_SLOT_UNBOUND)
+ return -ENOENT;
+ }
+
+ retry_count = (opt_tries && !opt_key_file) ? opt_tries : 1;
+ while (retry_count--) {
+ r = tools_get_key(msg, &password, &passwordLen, 0, 0,
+ opt_key_file, 0, 0, 0 /*pwquality*/, cd);
+ if (r < 0)
+ return r;
+ if (quit) {
+ crypt_safe_free(password);
+ password = NULL;
+ passwordLen = 0;
+ return -EAGAIN;
+ }
+
+ r = crypt_activate_by_passphrase(cd, NULL, slot_to_check,
+ password, passwordLen, 0);
+ if (r < 0) {
+ crypt_safe_free(password);
+ password = NULL;
+ passwordLen = 0;
+ }
+ if (r < 0 && r != -EPERM)
+ return r;
+
+ if (r >= 0) {
+ tools_keyslot_msg(r, UNLOCKED);
+ if ((size_t)r >= keyslot_passwords_length) {
+ crypt_safe_free(password);
+ return -EINVAL;
+ }
+ kp[r].password = password;
+ kp[r].passwordLen = passwordLen;
+ break;
+ }
+ tools_passphrase_msg(r);
+ }
+
+ password = NULL;
+ passwordLen = 0;
+
+ return r;
+}
+
+static int _check_luks2_keyslots(struct crypt_device *cd)
+{
+ int i, max = crypt_keyslot_max(CRYPT_LUKS2), active = 0, unbound = 0;
+
+ if (max < 0)
+ return max;
+
+ for (i = 0; i < max; i++) {
+ switch (crypt_keyslot_status(cd, i)) {
+ case CRYPT_SLOT_INVALID:
+ return -EINVAL;
+ case CRYPT_SLOT_ACTIVE:
+ /* fall-through */
+ case CRYPT_SLOT_ACTIVE_LAST:
+ active++;
+ break;
+ case CRYPT_SLOT_UNBOUND:
+ unbound++;
+ /* fall-through */
+ default:
+ break;
+ }
+ }
+
+ /* at least one keyslot for reencryption plus new volume key */
+ if (active + unbound > max - 2) {
+ log_err(_("Not enough free keyslots for reencryption."));
+ return -EINVAL;
+ }
+
+ if ((opt_key_slot == CRYPT_ANY_SLOT) &&
+ (2 * active + unbound > max - 1)) {
+ log_err(_("Not enough free keyslots for reencryption."));
+ return -EINVAL;
+ }
+
+ return 0;