summaryrefslogtreecommitdiffstats
path: root/misc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 17:44:12 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 17:44:12 +0000
commit1be69c2c660b70ac2f4de2a5326e27e3e60eb82d (patch)
treebb299ab6f411f4fccd735907035de710e4ec6abc /misc
parentInitial commit. (diff)
downloadcryptsetup-upstream/2%2.3.7.tar.xz
cryptsetup-upstream/2%2.3.7.zip
Adding upstream version 2:2.3.7.upstream/2%2.3.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'misc')
-rw-r--r--misc/11-dm-crypt.rules17
-rw-r--r--misc/dict_search/Makefile17
-rw-r--r--misc/dict_search/README22
-rw-r--r--misc/dict_search/crypt_dict.c158
-rw-r--r--misc/dracut_90reencrypt/README40
-rwxr-xr-xmisc/dracut_90reencrypt/check.old5
-rwxr-xr-xmisc/dracut_90reencrypt/install.old6
-rwxr-xr-xmisc/dracut_90reencrypt/module-setup.sh32
-rwxr-xr-xmisc/dracut_90reencrypt/parse-reencrypt.sh38
-rwxr-xr-xmisc/dracut_90reencrypt/reencrypt-verbose.sh6
-rwxr-xr-xmisc/dracut_90reencrypt/reencrypt.sh84
-rw-r--r--misc/keyslot_checker/Makefile14
-rw-r--r--misc/keyslot_checker/README120
-rw-r--r--misc/keyslot_checker/chk_luks_keyslots.c371
-rwxr-xr-xmisc/luks-header-from-active59
-rw-r--r--misc/luks2_keyslot_example/Makefile24
-rw-r--r--misc/luks2_keyslot_example/README3
-rw-r--r--misc/luks2_keyslot_example/keyslot_test.c409
-rw-r--r--misc/luks2_keyslot_example/keyslot_test_remote_pass.c264
19 files changed, 1689 insertions, 0 deletions
diff --git a/misc/11-dm-crypt.rules b/misc/11-dm-crypt.rules
new file mode 100644
index 0000000..dfbb3a0
--- /dev/null
+++ b/misc/11-dm-crypt.rules
@@ -0,0 +1,17 @@
+# Old udev rules historically used in device-mapper.
+# No need to install these until you have some weird configuration.
+# (Code internally set the same flags.)
+
+ACTION!="add|change", GOTO="crypt_end"
+ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="crypt_end"
+
+ENV{DM_UUID}=="CRYPT-TEMP-?*", GOTO="crypt_disable"
+ENV{DM_UUID}!="?*", ENV{DM_NAME}=="temporary-cryptsetup-?*", GOTO="crypt_disable"
+GOTO="crypt_end"
+
+LABEL="crypt_disable"
+ENV{DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG}="1"
+ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1"
+ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
+
+LABEL="crypt_end"
diff --git a/misc/dict_search/Makefile b/misc/dict_search/Makefile
new file mode 100644
index 0000000..0226c98
--- /dev/null
+++ b/misc/dict_search/Makefile
@@ -0,0 +1,17 @@
+TARGET=crypt_dict
+CFLAGS=-O2 -g -Wall -D_GNU_SOURCE
+LDLIBS=-lcryptsetup
+CC=gcc
+
+SOURCES=$(wildcard *.c)
+OBJECTS=$(SOURCES:.c=.o)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJECTS)
+ $(CC) -o $@ $^ $(LDLIBS)
+
+clean:
+ rm -f *.o *~ core $(TARGET)
+
+.PHONY: clean
diff --git a/misc/dict_search/README b/misc/dict_search/README
new file mode 100644
index 0000000..fc6aa44
--- /dev/null
+++ b/misc/dict_search/README
@@ -0,0 +1,22 @@
+Simple example how to use libcryptsetup
+for password search.
+
+Run: crypt_dict luks|tcrypt <device|image> <dictionary> [cpus]
+
+luks|tcrypt specified device type (LUKS or TrueCrypt)
+
+<device|image> is LUKS or TrueCrypt device or image
+
+<dictionary> is list of passphrases to try
+(note trailing EOL is stripped)
+
+cpus - number of processes to start in parallel
+
+Format of dictionary file is simple one password per line,
+if first char on line is # it is skipped as comment.
+
+For LUKS, you have it run as root (device-mapper cannot
+create dmcrypt devices as nrmal user. Code need
+to map keyslots as temporary dmcrypt device.)
+
+For TrueCrypt devices root privilege is not required.
diff --git a/misc/dict_search/crypt_dict.c b/misc/dict_search/crypt_dict.c
new file mode 100644
index 0000000..c80d502
--- /dev/null
+++ b/misc/dict_search/crypt_dict.c
@@ -0,0 +1,158 @@
+/*
+ * Example of LUKS/TrueCrypt password dictionary search
+ *
+ * Copyright (C) 2012 Milan Broz <gmazyland@gmail.com>
+ *
+ * Run this (for LUKS as root),
+ * e.g. ./crypt_dict test.img /usr/share/john/password.lst 4
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <libcryptsetup.h>
+
+#define MAX_LEN 512
+
+static enum { LUKS, TCRYPT } device_type;
+
+static void check(struct crypt_device *cd, const char *pwd_file, unsigned my_id, unsigned max_id)
+{
+ FILE *f;
+ int len, r = -1;
+ unsigned long line = 0;
+ char pwd[MAX_LEN];
+
+ if (fork())
+ return;
+
+ /* open password file, now in separate process */
+ f = fopen(pwd_file, "r");
+ if (!f) {
+ printf("Cannot open %s.\n", pwd_file);
+ exit(EXIT_FAILURE);
+ }
+
+ while (fgets(pwd, MAX_LEN, f)) {
+
+ /* every process tries N-th line, skip others */
+ if (line++ % max_id != my_id)
+ continue;
+
+ len = strlen(pwd);
+
+ /* strip EOL - this is like a input from tty */
+ if (len && pwd[len - 1] == '\n') {
+ pwd[len - 1] = '\0';
+ len--;
+ }
+
+ /* lines starting "#!comment" are comments */
+ if (len >= 9 && !strncmp(pwd, "#!comment", 9)) {
+ /* printf("skipping %s\n", pwd); */
+ continue;
+ }
+
+ /* printf("%d: checking %s\n", my_id, pwd); */
+ if (device_type == LUKS)
+ r = crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, pwd, len, 0);
+ else if (device_type == TCRYPT) {
+ struct crypt_params_tcrypt params = {
+ .flags = CRYPT_TCRYPT_LEGACY_MODES,
+ .passphrase = pwd,
+ .passphrase_size = len,
+ };
+ r = crypt_load(cd, CRYPT_TCRYPT, &params);
+ }
+ if (r >= 0) {
+ printf("Found passphrase for slot %d: \"%s\"\n", r, pwd);
+ break;
+ }
+ }
+
+ fclose(f);
+ crypt_free(cd);
+ exit(r >= 0 ? 2 : EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, status, procs = 4;
+ struct crypt_device *cd;
+
+ if (argc < 4 || argc > 5) {
+ printf("Use: %s luks|tcrypt <device|file> <password file> [#processes] %d\n", argv[0], argc);
+ exit(EXIT_FAILURE);
+ }
+
+ if (argc == 5 && (sscanf(argv[4], "%i", &procs) != 1 || procs < 1)) {
+ printf("Wrong number of processes.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!strcmp(argv[1], "luks"))
+ device_type = LUKS;
+ else if (!strcmp(argv[1], "tcrypt"))
+ device_type = TCRYPT;
+ else {
+ printf("Wrong device type %s.\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* crypt_set_debug_level(CRYPT_DEBUG_ALL); */
+
+ /*
+ * Need to create temporary keyslot device-mapper devices and allocate loop if needed,
+ * so root is required here.
+ */
+ if (getuid() != 0) {
+ printf("You must be root to run this program.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* signal all children if anything happens */
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ setpriority(PRIO_PROCESS, 0, -5);
+
+ /* we are not going to modify anything, so common init is ok */
+ if (crypt_init(&cd, argv[2]) ||
+ (device_type == LUKS && crypt_load(cd, CRYPT_LUKS1, NULL))) {
+ printf("Cannot open %s.\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* run scan in separate processes, it is up to scheduler to assign CPUs inteligently */
+ for (i = 0; i < procs; i++)
+ check(cd, argv[3], i, procs);
+
+ /* wait until at least one finishes with error or status 2 (key found) */
+ while (wait(&status) != -1 && WIFEXITED(status)) {
+ if (WEXITSTATUS(status) == EXIT_SUCCESS)
+ continue;
+ /* kill rest of processes */
+ kill(0, SIGHUP);
+ /* not reached */
+ break;
+ }
+ exit(0);
+}
diff --git a/misc/dracut_90reencrypt/README b/misc/dracut_90reencrypt/README
new file mode 100644
index 0000000..0672949
--- /dev/null
+++ b/misc/dracut_90reencrypt/README
@@ -0,0 +1,40 @@
+Example of simple dracut module for reencryption of system
+LUKS drive on-the-fly.
+
+Install in /usr/[share|lib]/dracut/modules.d/90reencrypt, then
+build special initramfs "with dracut -a reencrypt -o crypt".
+Reencrypt module doesn't work (has a conflict) with crypt module as
+of now. After successful reencryption reboot using original initramfs.
+
+Dracut then recognize argument rd.luks.reencrypt=name:size,
+e.g. rd.luks.reencrypt=sda2:52G means only 52G of device
+will be reencrypted (default is whole device).
+(Name is kernel name of device.)
+
+If there's more than single active keyslot in the target luks device
+you're required to select one keyslot explicitly for reencryption via
+rd.luks.reencrypt_keyslot=<keyslot_number> option. Bear in mind that
+if you use this option, all other keyslots will get deactivated in the
+process.
+
+Another argument, rd.luks.reencrypt_key=/dev/sda:/path/to/keyfile
+can be used to read password for specific keyslot from device containing
+filesystem with a keyfile (file with a password). If you omit reencrypt_key
+argument, reencryption would work only in case a LUKS container has
+exactly one keyslot activated.
+
+Arguments rd.luks.reencrypt_keyslot and rd.luks.reencrypt_key are not
+mandatory.
+
+Note that reencryption context is stored in ramdisk, any
+fail can mean complete lost of data!
+
+Copyright (C) 2012 Milan Broz <gmazyland@gmail.com>
+
+This copyrighted material is made available to anyone wishing to use,
+modify, copy, or redistribute it subject to the terms and conditions
+of the GNU General Public License v.2.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
diff --git a/misc/dracut_90reencrypt/check.old b/misc/dracut_90reencrypt/check.old
new file mode 100755
index 0000000..53010b3
--- /dev/null
+++ b/misc/dracut_90reencrypt/check.old
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+which cryptsetup-reencrypt >/dev/null 2>&1 || exit 1
+
+exit 0
diff --git a/misc/dracut_90reencrypt/install.old b/misc/dracut_90reencrypt/install.old
new file mode 100755
index 0000000..6e0523b
--- /dev/null
+++ b/misc/dracut_90reencrypt/install.old
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+inst cryptsetup-reencrypt
+
+inst_hook cmdline 30 "$moddir/parse-reencrypt.sh"
+inst "$moddir"/reencrypt.sh /sbin/reencrypt
diff --git a/misc/dracut_90reencrypt/module-setup.sh b/misc/dracut_90reencrypt/module-setup.sh
new file mode 100755
index 0000000..fcd7c92
--- /dev/null
+++ b/misc/dracut_90reencrypt/module-setup.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+check() {
+ [ -x /sbin/cryptsetup-reencrypt ] || return 1
+ return 255
+}
+
+depends() {
+ echo dm rootfs-block
+}
+
+installkernel() {
+ # requires hostonly='' override so that loop module is pulled in initramfs
+ # even if not loaded in actual kernel. dracut bug?
+ hostonly='' instmods dm_crypt =crypto loop
+}
+
+install() {
+ if dracut_module_included crypt; then
+ derror "'reencrypt' can't be installed together with 'crypt'."
+ derror "Add '-o crypt' option to install reencrypt module."
+ return 1
+ fi
+
+ dracut_install cryptsetup-reencrypt
+
+ # moddir variable is assigned in dracut general shell lib
+ # shellcheck disable=SC2154
+ inst_hook cmdline 30 "$moddir/parse-reencrypt.sh"
+ inst_simple "$moddir"/reencrypt.sh /sbin/reencrypt
+ inst_simple "$moddir"/reencrypt-verbose.sh /sbin/cryptsetup-reencrypt-verbose
+}
diff --git a/misc/dracut_90reencrypt/parse-reencrypt.sh b/misc/dracut_90reencrypt/parse-reencrypt.sh
new file mode 100755
index 0000000..5fec191
--- /dev/null
+++ b/misc/dracut_90reencrypt/parse-reencrypt.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+REENC=$(getargs rd.luks.reencrypt=)
+# shellcheck disable=SC2086
+REENC_DEV=$(echo $REENC | sed 's/:.*//')
+# shellcheck disable=SC2086
+REENC_SIZE=$(echo $REENC | sed -n 's/.*://p')
+
+REENC_KEY=$(getargs rd.luks.reencrypt_key=)
+if [ -z "$REENC_KEY" ] ; then
+ REENC_KEY=none
+fi
+
+REENC_SLOT=$(getargs rd.luks.reencrypt_keyslot=)
+if [ -z "$REENC_SLOT" ] ; then
+ REENC_SLOT=any
+fi
+
+# shellcheck disable=SC2086
+# shellcheck disable=SC1004
+# shellcheck disable=SC2016
+if [ -n "$REENC_DEV" ] ; then
+{
+ printf 'SUBSYSTEM!="block", GOTO="reenc_end"\n'
+ printf 'ACTION!="add|change", GOTO="reenc_end"\n'
+ printf 'KERNEL=="%s", ' $REENC_DEV
+ printf 'ENV{ID_FS_TYPE}=="crypto_LUKS", RUN+="/sbin/initqueue \
+ --unique --onetime --settled --name crypt-reencrypt-%%k \
+ /sbin/reencrypt $env{DEVNAME} %s"\n' "$REENC_KEY $REENC_SLOT $REENC_SIZE"
+
+ printf 'ENV{ID_FS_UUID}=="*%s*", ' $REENC_DEV
+ printf 'ENV{ID_FS_TYPE}=="crypto_LUKS", RUN+="/sbin/initqueue \
+ --unique --onetime --settled --name crypt-reencrypt-%%k \
+ /sbin/reencrypt $env{DEVNAME} %s"\n' "$REENC_KEY $REENC_SLOT $REENC_SIZE"
+ printf 'LABEL="reenc_end"\n'
+} > /etc/udev/rules.d/69-reencryption.rules
+ initqueue --unique --finished --name crypt-reencrypt-finished-${REENC_DEV} [ -e /tmp/reencrypted ]
+fi
diff --git a/misc/dracut_90reencrypt/reencrypt-verbose.sh b/misc/dracut_90reencrypt/reencrypt-verbose.sh
new file mode 100755
index 0000000..109ce6e
--- /dev/null
+++ b/misc/dracut_90reencrypt/reencrypt-verbose.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# Route stdout to stderr in initrd. Otherwise output is invisible
+# unless we run in debug mode.
+# shellcheck disable=SC2068
+/sbin/cryptsetup-reencrypt $@ 1>&2
diff --git a/misc/dracut_90reencrypt/reencrypt.sh b/misc/dracut_90reencrypt/reencrypt.sh
new file mode 100755
index 0000000..db09e64
--- /dev/null
+++ b/misc/dracut_90reencrypt/reencrypt.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+#
+# $1=$device [$2=keyfile|none [$3=keyslot|any [$4=size]]]
+#
+
+[ -d /sys/module/dm_crypt ] || modprobe dm_crypt
+
+[ -d /sys/module/loop ] || modprobe loop
+
+[ -f /tmp/reencrypted ] && exit 0
+
+. /lib/dracut-lib.sh
+
+# if device name is /dev/dm-X, convert to /dev/mapper/name
+if [ "${1##/dev/dm-}" != "$1" ]; then
+ device="/dev/mapper/$(dmsetup info -c --noheadings -o name "$1")"
+else
+ device="$1"
+fi
+
+PARAMS="$device -T 1 --use-fsync --progress-frequency 5 -B 32"
+if [ "$3" != "any" ]; then
+ PARAMS="$PARAMS -S $3"
+fi
+
+if [ -n "$4" ]; then
+ PARAMS="$PARAMS --device-size $4"
+fi
+
+reenc_readkey() {
+ keypath="${1#*:}"
+ keydev="${1%%:*}"
+
+ mntp="/tmp/reencrypted-mount-tmp"
+ mkdir "$mntp"
+ mount -r "$keydev" "$mntp" && cat "$mntp/$keypath"
+ umount "$mntp"
+ rm -r "$mntp"
+}
+
+# shellcheck disable=SC2086
+# shellcheck disable=SC2164
+reenc_run() {
+ cwd=$(pwd)
+ _prompt="LUKS password for REENCRYPTING $device"
+ cd /tmp
+ udevadm settle
+ if [ "$1" = "none" ] ; then
+ if [ "$2" != "any" ]; then
+ _prompt="$_prompt, using keyslot $2"
+ fi
+ /bin/plymouth ask-for-password \
+ --prompt "$_prompt" \
+ --command="/sbin/cryptsetup-reencrypt-verbose $PARAMS"
+ else
+ info "REENCRYPT using key $1"
+ reenc_readkey "$1" | /sbin/cryptsetup-reencrypt-verbose -d - $PARAMS
+ fi
+ _ret=$?
+ cd $cwd
+}
+
+info "REENCRYPT $device requested"
+# flock against other interactive activities
+# shellcheck disable=SC2086
+{ flock -s 9;
+ reenc_run $2 $3
+} 9>/.console_lock
+
+if [ $_ret -eq 0 ]; then
+ # do not ask again
+ # shellcheck disable=SC2188
+ >> /tmp/reencrypted
+ warn "Reencryption of device $device has finished successfully. Use previous"
+ warn "initramfs image (without reencrypt module) to boot the system. When"
+ warn "you leave the emergency shell, the system will reboot."
+
+ emergency_shell -n "(reboot)"
+ [ -x /usr/bin/systemctl ] && /usr/bin/systemctl reboot
+ [ -x /sbin/shutdown ] && /sbin/shutdown -r now
+fi
+
+# panic the kernel otherwise
+exit 1
diff --git a/misc/keyslot_checker/Makefile b/misc/keyslot_checker/Makefile
new file mode 100644
index 0000000..3b159fd
--- /dev/null
+++ b/misc/keyslot_checker/Makefile
@@ -0,0 +1,14 @@
+TARGETS=chk_luks_keyslots
+CFLAGS=-O0 -g -Wall -D_GNU_SOURCE
+LDLIBS=-lcryptsetup -lm
+CC=gcc
+
+all: $(TARGETS)
+
+chk_luks_keyslots: chk_luks_keyslots.o
+ $(CC) -o $@ $^ $(LDLIBS)
+
+clean:
+ rm -f *.o *~ core $(TARGETS)
+
+.PHONY: clean
diff --git a/misc/keyslot_checker/README b/misc/keyslot_checker/README
new file mode 100644
index 0000000..cd5bf81
--- /dev/null
+++ b/misc/keyslot_checker/README
@@ -0,0 +1,120 @@
+Purpose
+=======
+
+chk_luks_keyslots is a tool that searches the keyslot area of a
+LUKS container for positions where entropy is low and hence
+there is a high probability of damage from overwrites of parts
+of the key-slot with data such as a RAID superblock or a partition
+table.
+
+
+Installation
+============
+
+1. Install the version of cryptsetup the tool came with.
+2. Compile with "make"
+
+Manual compile can be done with
+ gcc -lm -lcryptsetup chk_luks_keyslots.c -o chk_luks_keyslots
+
+Usage
+=====
+
+Call chk_luks_keyslots without arguments for an option summary.
+
+
+Example of a good keyslot area with keys 0 and 2 in use:
+--------------------------------------------------------
+
+root> ./chk_luks_keyslots /dev/loop0
+
+parameters (commandline and LUKS header):
+ sector size: 512
+ threshold: 0.900000
+
+- processing keyslot 0: start: 0x001000 end: 0x020400
+- processing keyslot 1: keyslot not in use
+- processing keyslot 2: start: 0x041000 end: 0x060400
+- processing keyslot 3: keyslot not in use
+- processing keyslot 4: keyslot not in use
+- processing keyslot 5: keyslot not in use
+- processing keyslot 6: keyslot not in use
+- processing keyslot 7: keyslot not in use
+
+
+Same example of a fault in slot 2 at offset 0x50000:
+----------------------------------------------------
+
+root>./chk_luks_keyslots /dev/loop2
+
+parameters (commandline and LUKS header):
+ sector size: 512
+ threshold: 0.900000
+
+- processing keyslot 0: start: 0x001000 end: 0x020400
+- processing keyslot 1: keyslot not in use
+- processing keyslot 2: start: 0x041000 end: 0x060400
+ low entropy at: 0x050000 entropy: 0.549165
+- processing keyslot 3: keyslot not in use
+- processing keyslot 4: keyslot not in use
+- processing keyslot 5: keyslot not in use
+- processing keyslot 6: keyslot not in use
+- processing keyslot 7: keyslot not in use
+
+
+Same as last, but verbose:
+--------------------------
+root>./chk_luks_keyslots -v /dev/loop2
+
+parameters (commandline and LUKS header):
+ sector size: 512
+ threshold: 0.900000
+
+- processing keyslot 0: start: 0x001000 end: 0x020400
+- processing keyslot 1: keyslot not in use
+- processing keyslot 2: start: 0x041000 end: 0x060400
+ low entropy at: 0x050000 entropy: 0.549165
+ Binary dump:
+ 0x050000 54 68 69 73 20 69 73 20 61 20 74 65 73 74 2D 73 This is a test-s
+ 0x050010 65 63 74 6F 72 20 66 6F 72 20 63 68 6B 5F 6C 75 ector for chk_lu
+ 0x050020 6B 73 5F 6B 65 79 73 6C 6F 74 73 20 74 68 65 20 ks_keyslots the
+ 0x050030 71 75 69 63 6B 20 62 72 6F 77 6E 20 66 6F 78 20 quick brown fox
+ 0x050040 6A 75 6D 70 73 20 6F 76 65 72 20 74 68 65 20 6C jumps over the l
+ 0x050050 61 7A 79 20 64 6F 67 20 74 68 65 20 71 75 69 63 azy dog the quic
+ 0x050060 6B 20 62 72 6F 77 6E 20 66 6F 78 20 6A 75 6D 70 k brown fox jump
+ 0x050070 73 20 6F 76 65 72 20 74 68 65 20 6C 61 7A 79 20 s over the lazy
+ 0x050080 64 6F 67 20 74 68 65 20 71 75 69 63 6B 20 62 72 dog the quick br
+ 0x050090 6F 77 6E 20 66 6F 78 20 6A 75 6D 70 73 20 6F 76 own fox jumps ov
+ 0x0500a0 65 72 20 74 68 65 20 6C 61 7A 79 20 64 6F 67 20 er the lazy dog
+ 0x0500b0 74 68 65 20 71 75 69 63 6B 20 62 72 6F 77 6E 20 the quick brown
+ 0x0500c0 66 6F 78 20 6A 75 6D 70 73 20 6F 76 65 72 20 74 fox jumps over t
+ 0x0500d0 68 65 20 6C 61 7A 79 20 64 6F 67 20 74 68 65 20 he lazy dog the
+ 0x0500e0 71 75 69 63 6B 20 62 72 6F 77 6E 20 66 6F 78 20 quick brown fox
+ 0x0500f0 6A 75 6D 70 73 20 6F 76 65 72 20 74 68 65 20 6C jumps over the l
+ 0x050100 61 7A 79 20 64 6F 67 20 74 68 65 20 71 75 69 63 azy dog the quic
+ 0x050110 6B 20 62 72 6F 77 6E 20 66 6F 78 20 6A 75 6D 70 k brown fox jump
+ 0x050120 73 20 6F 76 65 72 20 74 68 65 20 6C 61 7A 79 20 s over the lazy
+ 0x050130 64 6F 67 20 74 68 65 20 71 75 69 63 6B 20 62 72 dog the quick br
+ 0x050140 6F 77 6E 20 66 6F 78 20 6A 75 6D 70 73 20 6F 76 own fox jumps ov
+ 0x050150 65 72 20 74 68 65 20 6C 61 7A 79 20 64 6F 67 20 er the lazy dog
+ 0x050160 74 68 65 20 71 75 69 63 6B 20 62 72 6F 77 6E 20 the quick brown
+ 0x050170 66 6F 78 20 6A 75 6D 70 73 20 6F 76 65 72 20 74 fox jumps over t
+ 0x050180 68 65 20 6C 61 7A 79 20 64 6F 67 20 74 68 65 20 he lazy dog the
+ 0x050190 71 75 69 63 6B 20 62 72 6F 77 6E 20 66 6F 78 20 quick brown fox
+ 0x0501a0 6A 75 6D 70 73 20 6F 76 65 72 20 74 68 65 20 6C jumps over the l
+ 0x0501b0 61 7A 79 20 64 6F 67 20 74 68 65 20 71 75 69 63 azy dog the quic
+ 0x0501c0 6B 20 62 72 6F 77 6E 20 66 6F 78 20 6A 75 6D 70 k brown fox jump
+ 0x0501d0 73 20 6F 76 65 72 20 74 68 65 20 6C 61 7A 79 20 s over the lazy
+ 0x0501e0 64 6F 67 20 74 68 65 20 71 75 69 63 6B 20 62 72 dog the quick br
+ 0x0501f0 6F 77 6E 20 66 6F 78 20 6A 75 6D 70 73 20 6F 76 own fox jumps ov
+
+- processing keyslot 3: keyslot not in use
+- processing keyslot 4: keyslot not in use
+- processing keyslot 5: keyslot not in use
+- processing keyslot 6: keyslot not in use
+- processing keyslot 7: keyslot not in use
+
+----
+Copyright (C) 2012, Arno Wagner <arno@wagner.name>
+This file is free documentation; the author gives
+unlimited permission to copy, distribute and modify it.
diff --git a/misc/keyslot_checker/chk_luks_keyslots.c b/misc/keyslot_checker/chk_luks_keyslots.c
new file mode 100644
index 0000000..d05aad8
--- /dev/null
+++ b/misc/keyslot_checker/chk_luks_keyslots.c
@@ -0,0 +1,371 @@
+/*
+ * LUKS keyslot entropy tester. Works only for header version 1.
+ *
+ * Functionality: Determines sample entropy (symbols: bytes) for
+ * each (by default) 512B sector in each used keyslot. If it
+ * is lower than a threshold, the sector address is printed
+ * as it is suspected of having non-"random" data in it, indicating
+ * damage by overwriting. This can obviously not find overwriting
+ * with random or random-like data (encrypted, compressed).
+ *
+ * Version history:
+ * v0.1: 09.09.2012 Initial release
+ * v0.2: 08.10.2012 Converted to use libcryptsetup
+ *
+ * Copyright (C) 2012, Arno Wagner <arno@wagner.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <math.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libcryptsetup.h>
+
+const char *help =
+"Version 0.2 [8.10.2012]\n"
+"\n"
+" chk_luks_keyslots [options] luks-device \n"
+"\n"
+"This tool checks all keyslots of a LUKS device for \n"
+"low entropy sections. If any are found, they are reported. \n"
+"This allows to find areas damaged by things like filesystem \n"
+"creation or RAID superblocks. \n"
+"\n"
+"Options: \n"
+" -t <num> Entropy threshold. Possible values 0.0 ... 1.0 \n"
+" Default: 0.90, which works well for 512B sectors.\n"
+" For 512B sectors, you will get frequent misdetections\n"
+" at thresholds around 0.94\n"
+" Higher value: more sensitive but more false detections.\n"
+" -s <num> Sector size. Must divide keyslot-size.\n"
+" Default: 512 Bytes.\n"
+" Values smaller than 128 are generally not very useful.\n"
+" For values smaller than the default, you need to adjust\n"
+" the threshold down to reduce misdetection. For values\n"
+" larger than the default you need to adjust the threshold\n"
+" up to retain sensitivity.\n"
+" -v Print found suspicious sectors verbosely. \n"
+" -d Print decimal addresses instead of hex ones.\n"
+"\n";
+
+
+/* Config defaults */
+
+static int sector_size = 512;
+static double threshold = 0.90;
+static int print_decimal = 0;
+static int verbose = 0;
+
+/* tools */
+
+/* Calculates and returns sample entropy on byte level for
+ * The argument.
+ */
+static double ent_samp(unsigned char * buf, int len)
+{
+ int freq[256]; /* stores symbol frequencies */
+ int i;
+ double e, f;
+
+ /* 0. Plausibility checks */
+ if (len <= 0)
+ return 0.0;
+
+ /* 1. count all frequencies */
+ for (i = 0; i < 256; i++) {
+ freq[i] = 0.0;
+ }
+
+ for (i = 0; i < len; i ++)
+ freq[buf[i]]++;
+
+ /* 2. calculate sample entropy */
+ e = 0.0;
+ for (i = 0; i < 256; i++) {
+ f = freq[i];
+ if (f > 0) {
+ f = f / (double)len;
+ e += f * log2(f);
+ }
+ }
+
+ if (e != 0.0)
+ e = -1.0 * e;
+
+ e = e / 8.0;
+ return e;
+}
+
+static void print_address(FILE *out, uint64_t value)
+{
+ if (print_decimal) {
+ fprintf(out,"%08" PRIu64 " ", value);
+ } else {
+ fprintf(out,"%#08" PRIx64 " ", value);
+ }
+}
+
+/* uses default "hd" style, i.e. 16 bytes followed by ASCII */
+static void hexdump_line(FILE *out, uint64_t address, unsigned char *buf) {
+ int i;
+ static char tbl[16] = "0123456789ABCDEF";
+
+ fprintf(out," ");
+ print_address(out, address);
+ fprintf(out," ");
+
+ /* hex */
+ for (i = 0; i < 16; i++) {
+ fprintf(out, "%c%c",
+ tbl[(unsigned char)buf[i]>> 4],
+ tbl[(unsigned char)buf[i] & 0x0f]);
+ fprintf(out," ");
+ if (i == 7)
+ fprintf(out," ");
+ }
+
+ fprintf(out," ");
+
+ /* ascii */
+ for (i = 0; i < 16; i++) {
+ if (isprint(buf[i])) {
+ fprintf(out, "%c", buf[i]);
+ } else {
+ fprintf(out, ".");
+ }
+ }
+ fprintf(out, "\n");
+}
+
+static void hexdump_sector(FILE *out, unsigned char *buf, uint64_t address, int len)
+{
+ int done;
+
+ done = 0;
+ while (len - done >= 16) {
+ hexdump_line(out, address + done, buf + done);
+ done += 16;
+ }
+}
+
+static int check_keyslots(FILE *out, struct crypt_device *cd, int f_luks)
+{
+ int i;
+ double ent;
+ off_t ofs;
+ uint64_t start, length, end;
+ crypt_keyslot_info ki;
+ unsigned char buffer[sector_size];
+
+ for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS1) ; i++) {
+ fprintf(out, "- processing keyslot %d:", i);
+ ki = crypt_keyslot_status(cd, i);
+ if (ki == CRYPT_SLOT_INACTIVE) {
+ fprintf(out, " keyslot not in use\n");
+ continue;
+ }
+
+ if (ki == CRYPT_SLOT_INVALID) {
+ fprintf(out, "\nError: keyslot invalid.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (crypt_keyslot_area(cd, i, &start, &length) < 0) {
+ fprintf(stderr,"\nError: querying keyslot area failed for slot %d\n", i);
+ perror(NULL);
+ return EXIT_FAILURE;
+ }
+ end = start + length;
+
+ fprintf(out, " start: ");
+ print_address(out, start);
+ fprintf(out, " end: ");
+ print_address(out, end);
+ fprintf(out, "\n");
+
+ /* check whether sector-size divides size */
+ if (length % sector_size != 0) {
+ fprintf(stderr,"\nError: Argument to -s does not divide keyslot size\n");
+ return EXIT_FAILURE;
+ }
+
+ for (ofs = start; (uint64_t)ofs < end; ofs += sector_size) {
+ if (lseek(f_luks, ofs, SEEK_SET) != ofs) {
+ fprintf(stderr,"\nCannot seek to keyslot area.\n");
+ return EXIT_FAILURE;
+ }
+ if (read(f_luks, buffer, sector_size) != sector_size) {
+ fprintf(stderr,"\nCannot read keyslot area.\n");
+ return EXIT_FAILURE;
+ }
+ ent = ent_samp(buffer, sector_size);
+ if (ent < threshold) {
+ fprintf(out, " low entropy at: ");
+ print_address(out, ofs);
+ fprintf(out, " entropy: %f\n", ent);
+ if (verbose) {
+ fprintf(out, " Binary dump:\n");
+ hexdump_sector(out, buffer, (uint64_t)ofs, sector_size);
+ fprintf(out,"\n");
+ }
+ }
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+/* Main */
+int main(int argc, char **argv)
+{
+ /* for option processing */
+ int c, r;
+ char *device;
+
+ /* for use of libcryptsetup */
+ struct crypt_device *cd;
+
+ /* Other vars */
+ int f_luks; /* device file for the luks device */
+ FILE *out;
+
+ /* temporary helper vars */
+ int res;
+
+ /* getopt values */
+ char *s, *end;
+ double tvalue;
+ int svalue;
+
+ /* global initializations */
+ out = stdout;
+
+ /* get commandline parameters */
+ while ((c = getopt (argc, argv, "t:s:vd")) != -1) {
+ switch (c) {
+ case 't':
+ s = optarg;
+ tvalue = strtod(s, &end);
+ if (s == end) {
+ fprintf(stderr, "\nError: Parsing of argument to -t failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (tvalue < 0.0 || tvalue > 1.0) {
+ fprintf(stderr,"\nError: Argument to -t must be in 0.0 ... 1.0\n");
+ exit(EXIT_FAILURE);
+ }
+ threshold = tvalue;
+ break;
+ case 's':
+ s = optarg;
+ svalue = strtol(s, &end, 10);
+ if (s == end) {
+ fprintf(stderr, "\nError: Parsing of argument to -s failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (svalue < 1) {
+ fprintf(stderr,"\nError: Argument to -s must be >= 1 \n");
+ exit(EXIT_FAILURE);
+ }
+ sector_size = svalue;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'd':
+ print_decimal = 1;
+ break;
+ case '?':
+ if (optopt == 't' || optopt == 's')
+ fprintf (stderr,"\nError: Option -%c requires an argument.\n",
+ optopt);
+ else if (isprint (optopt)) {
+ fprintf(stderr,"\nError: Unknown option `-%c'.\n", optopt);
+ fprintf(stderr,"\n\n%s", help);
+ } else {
+ fprintf (stderr, "\nError: Unknown option character `\\x%x'.\n",
+ optopt);
+ fprintf(stderr,"\n\n%s", help);
+ }
+ exit(EXIT_SUCCESS);
+ default:
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* parse non-option stuff. Should be exactly one, the device. */
+ if (optind+1 != argc) {
+ fprintf(stderr,"\nError: exactly one non-option argument expected!\n");
+ fprintf(stderr,"\n\n%s", help);
+ exit(EXIT_FAILURE);
+ }
+ device = argv[optind];
+
+ /* test whether we can open and read device */
+ /* This is needed as we are reading the actual data
+ * in the keyslots directly from the LUKS container.
+ */
+ f_luks = open(device, O_RDONLY);
+ if (f_luks == -1) {
+ fprintf(stderr,"\nError: Opening of device %s failed:\n", device);
+ perror(NULL);
+ exit(EXIT_FAILURE);
+ }
+
+ /* now get the parameters we need via libcryptsetup */
+ /* Basically we need all active keyslots and their placement on disk */
+
+ /* first init. This does the following:
+ * - gets us a crypt_device struct with some values filled in
+ * Note: This does some init stuff we do not need, but that
+ * should not cause trouble.
+ */
+
+ res = crypt_init(&cd, device);
+ if (res < 0) {
+ fprintf(stderr, "crypt_init() failed. Maybe not running as root?\n");
+ close(f_luks);
+ exit(EXIT_FAILURE);
+ }
+
+ /* now load LUKS header into the crypt_device
+ * This should also make sure a valid LUKS1 header is on disk
+ * and hence we should be able to skip magic and version checks.
+ */
+ res = crypt_load(cd, CRYPT_LUKS1, NULL);
+ if (res < 0) {
+ fprintf(stderr, "crypt_load() failed. LUKS header too broken/absent?\n");
+ crypt_free(cd);
+ close(f_luks);
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(out, "\nparameters (commandline and LUKS header):\n");
+ fprintf(out, " sector size: %d\n", sector_size);
+ fprintf(out, " threshold: %0f\n\n", threshold);
+
+ r = check_keyslots(out, cd, f_luks);
+
+ crypt_free(cd);
+ close(f_luks);
+ return r;
+}
diff --git a/misc/luks-header-from-active b/misc/luks-header-from-active
new file mode 100755
index 0000000..a94ad33
--- /dev/null
+++ b/misc/luks-header-from-active
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# Try to get LUKS info and master key from active mapping and prepare parameters for cryptsetup.
+#
+# Copyright (C) 2010,2011,2012 Milan Broz <gmazyland@gmail.com>
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+umask 0077
+
+fail() { echo -e $1 ; exit 1 ; }
+field() { echo $(dmsetup table --target crypt --showkeys $DEVICE | sed 's/.*: //' | cut -d' ' -f$1) ; }
+field_uuid() { echo $(dmsetup info $1 --noheadings -c -o uuid) ; }
+field_device() {
+ TEMP=$(readlink /sys/dev/block/$1 | sed -e 's/.*\///')
+ if [ ${TEMP:0:3} = "dm-" -a -e /sys/block/$TEMP/dm/name ] ; then
+ TEMP=/dev/mapper/$(cat /sys/block/$TEMP/dm/name)
+ else
+ TEMP=/dev/$TEMP
+ fi
+ echo $TEMP
+}
+
+which readlink >/dev/null || fail "You need readlink (part of coreutils package)."
+which xxd >/dev/null || fail "You need xxd (part of vim package) installed to convert key."
+
+[ -z "$2" ] && fail "Recover LUKS header from active mapping, use:\n $0 crypt_mapped_device mk_file_name"
+
+DEVICE=$1
+MK_FILE=$2
+
+[ -z "$(field 4)" ] && fail "Mapping $1 not active or it is not crypt target."
+
+CIPHER=$(field 4)
+OFFSET=$(field 8)
+SYS_DEVICE=$(field 7)
+REAL_DEVICE=$(field_device $SYS_DEVICE)
+KEY=$(field 5)
+KEY_SIZE=$(( ${#KEY} / 2 * 8 ))
+SYS_UUID=$(field_uuid $DEVICE)
+UUID="${SYS_UUID:12:8}-${SYS_UUID:20:4}-${SYS_UUID:24:4}-${SYS_UUID:28:4}-${SYS_UUID:32:12}"
+
+#echo "CIPHER=$CIPHER OFFSET=$OFFSET SYS_DEVICE=$SYS_DEVICE REAL_DEVICE=$REAL_DEVICE KEY_SIZE=$KEY_SIZE KEY=$KEY UUID=$UUID SYS_UUID=$SYS_UUID"
+
+[ -z "$CIPHER" -o -z "$OFFSET" -o "$OFFSET" -le 383 -o \
+-z "$KEY" -o -z "$UUID" -o -z "$REAL_DEVICE" -o "${SYS_UUID:0:12}" != "CRYPT-LUKS1-" ] && \
+fail "Incompatible device, sorry."
+
+echo "Generating master key to file $MK_FILE."
+echo -E -n $KEY| xxd -r -p >$MK_FILE
+
+echo "You can now try to reformat LUKS device using:"
+echo " cryptsetup luksFormat -c $CIPHER -s $KEY_SIZE --align-payload=$OFFSET --master-key-file=$MK_FILE --uuid=$UUID $REAL_DEVICE"
diff --git a/misc/luks2_keyslot_example/Makefile b/misc/luks2_keyslot_example/Makefile
new file mode 100644
index 0000000..777eafa
--- /dev/null
+++ b/misc/luks2_keyslot_example/Makefile
@@ -0,0 +1,24 @@
+TARGET=keyslot_test
+CFLAGS=-O0 -g -Wall -D_GNU_SOURCE -I ../../lib/
+LDLIBS=-ljson-c -luuid -lgcrypt -ldevmapper -lpthread -lssh
+CC=gcc
+
+TARGET2=keyslot_test_remote_pass
+
+SOURCES=keyslot_test.c
+OBJECTS=$(SOURCES:.c=.o)
+SOURCES2=keyslot_test_remote_pass.c
+OBJECTS2=$(SOURCES2:.c=.o)
+
+all: $(TARGET) $(TARGET2) $(TARGET4)
+
+$(TARGET): $(OBJECTS) ../../.libs/libcryptsetup.a
+ $(CC) -o $@ $^ $(LDLIBS)
+
+$(TARGET2): $(OBJECTS2) ../../.libs/libcryptsetup.a
+ $(CC) -o $@ $^ $(LDLIBS)
+
+clean:
+ rm -f *.o *~ core $(TARGET) $(TARGET2)
+
+.PHONY: clean
diff --git a/misc/luks2_keyslot_example/README b/misc/luks2_keyslot_example/README
new file mode 100644
index 0000000..ac4ab6b
--- /dev/null
+++ b/misc/luks2_keyslot_example/README
@@ -0,0 +1,3 @@
+to create LUKSv2 container run:
+
+$(top_level_dir)/src/cryptsetup --type luks2 luksFormat /dev/xxx
diff --git a/misc/luks2_keyslot_example/keyslot_test.c b/misc/luks2_keyslot_example/keyslot_test.c
new file mode 100644
index 0000000..5b8cc9a
--- /dev/null
+++ b/misc/luks2_keyslot_example/keyslot_test.c
@@ -0,0 +1,409 @@
+/*
+ * Example of LUKS2 kesylot handler (EXAMPLE)
+ *
+ * Copyright (C) 2016-2021 Milan Broz <gmazyland@gmail.com>
+ *
+ * Use:
+ * - generate LUKS device
+ * - store passphrase used in previous step remotely (single line w/o \r\n)
+ * - add new token using this example
+ * - activate device by token
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <json-c/json.h>
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
+#include "libcryptsetup.h"
+
+#define TOKEN_NUM 0
+
+#define PASSWORD_LENGTH 8192
+
+typedef int (*password_cb_func) (char **password);
+
+static json_object *get_token_jobj(struct crypt_device *cd, int token)
+{
+ const char *json_slot;
+
+ /* libcryptsetup API call */
+ if (crypt_token_json_get(cd, token, &json_slot))
+ return NULL;
+
+ return json_tokener_parse(json_slot);
+}
+
+static int download_remote_password(struct crypt_device *cd, ssh_session ssh,
+ const char *path, char **password,
+ size_t *password_len)
+{
+ char *pass;
+ size_t pass_len;
+ int r;
+ sftp_attributes sftp_attr = NULL;
+ sftp_session sftp = NULL;
+ sftp_file file = NULL;
+
+
+ sftp = sftp_new(ssh);
+ if (!sftp) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Cannot create sftp session: ");
+ r = SSH_FX_FAILURE;
+ goto out;
+ }
+
+ r = sftp_init(sftp);
+ if (r != SSH_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Cannot init sftp session: ");
+ goto out;
+ }
+
+ file = sftp_open(sftp, path, O_RDONLY, 0);
+ if (!file) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Cannot create sftp session: ");
+ r = SSH_FX_FAILURE;
+ goto out;
+ }
+
+ sftp_attr = sftp_fstat(file);
+ if (!sftp_attr) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Cannot stat sftp file: ");
+ r = SSH_FX_FAILURE;
+ goto out;
+ }
+
+ pass_len = sftp_attr->size > PASSWORD_LENGTH ? PASSWORD_LENGTH : sftp_attr->size;
+ pass = malloc(pass_len);
+ if (!pass) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Not enough memory.\n");
+ r = SSH_FX_FAILURE;
+ goto out;
+ }
+
+ r = sftp_read(file, pass, pass_len);
+ if (r < 0 || (size_t)r != pass_len) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Cannot read remote key: ");
+ r = SSH_FX_FAILURE;
+ goto out;
+ }
+
+ *password = pass;
+ *password_len = pass_len;
+
+ r = SSH_OK;
+out:
+ if (r != SSH_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, ssh_get_error(ssh));
+ crypt_log(cd, CRYPT_LOG_ERROR, "\n");
+ free(pass);
+ }
+
+ if (sftp_attr)
+ sftp_attributes_free(sftp_attr);
+
+ if (file)
+ sftp_close(file);
+ if (sftp)
+ sftp_free(sftp);
+ return r == SSH_OK ? 0 : -EINVAL;
+}
+
+static ssh_session ssh_session_init(struct crypt_device *cd,
+ const char *host,
+ const char *user)
+{
+ int r, port = 22;
+ ssh_session ssh = ssh_new();
+ if (!ssh)
+ return NULL;
+
+ ssh_options_set(ssh, SSH_OPTIONS_HOST, host);
+ ssh_options_set(ssh, SSH_OPTIONS_USER, user);
+ ssh_options_set(ssh, SSH_OPTIONS_PORT, &port);
+
+ crypt_log(cd, CRYPT_LOG_NORMAL, "Initiating ssh session.\n");
+
+ r = ssh_connect(ssh);
+ if (r != SSH_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Connection failed: ");
+ goto out;
+ }
+
+ r = ssh_is_server_known(ssh);
+ if (r != SSH_SERVER_KNOWN_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Server not known: ");
+ r = SSH_AUTH_ERROR;
+ goto out;
+ }
+
+ r = SSH_OK;
+
+ /* initialise list of authentication methods. yes, according to official libssh docs... */
+ ssh_userauth_none(ssh, NULL);
+out:
+ if (r != SSH_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, ssh_get_error(ssh));
+ crypt_log(cd, CRYPT_LOG_ERROR, "\n");
+ ssh_disconnect(ssh);
+ ssh_free(ssh);
+ ssh = NULL;
+ }
+
+ return ssh;
+}
+
+static void ssh_session_close(ssh_session ssh)
+{
+ if (ssh) {
+ ssh_disconnect(ssh);
+ ssh_free(ssh);
+ }
+}
+
+static int _public_key_auth(struct crypt_device *cd, ssh_session ssh)
+{
+ int r;
+ ssh_key pkey = NULL;
+
+ crypt_log(cd, CRYPT_LOG_DEBUG, "Trying public key authentication method.\n");
+
+ if (!(ssh_userauth_list(ssh, NULL) & SSH_AUTH_METHOD_PUBLICKEY)) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Public key auth method not allowed on host.\n");
+ return SSH_AUTH_ERROR;
+ }
+
+ r = ssh_pki_import_privkey_file("/home/user/.ssh/id_rsa", NULL, NULL, NULL, &pkey);
+ if (r != SSH_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Failed to import private key\n");
+
+ return r;
+ }
+
+ r = ssh_userauth_try_publickey(ssh, NULL, pkey);
+ if (r == SSH_AUTH_SUCCESS) {
+ crypt_log(cd, CRYPT_LOG_DEBUG, "Public key method accepted.\n");
+ r = ssh_userauth_publickey(ssh, NULL, pkey);
+ }
+
+ ssh_key_free(pkey);
+
+ if (r != SSH_AUTH_SUCCESS) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Public key authentication error: ");
+ crypt_log(cd, CRYPT_LOG_ERROR, ssh_get_error(ssh));
+ crypt_log(cd, CRYPT_LOG_ERROR, "\n");
+ }
+
+ return r;
+}
+
+static int _password_auth(struct crypt_device *cd, ssh_session ssh, password_cb_func pcb)
+{
+ int r = SSH_AUTH_ERROR;
+ char *ssh_password = NULL;
+
+ if (!(ssh_userauth_list(ssh, NULL) & SSH_AUTH_METHOD_PASSWORD)) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Password auth method not allowed on host.\n");
+ return r;
+ }
+
+ if (pcb(&ssh_password)) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Failed to process password.\n");
+ return r;
+ }
+
+ r = ssh_userauth_password(ssh, NULL, ssh_password);
+
+ free(ssh_password);
+
+ if (r != SSH_AUTH_SUCCESS) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Password authentication error: ");
+ crypt_log(cd, CRYPT_LOG_ERROR, ssh_get_error(ssh));
+ crypt_log(cd, CRYPT_LOG_ERROR, "\n");
+ }
+
+ return r;
+}
+
+static int SSHTEST_token_open(struct crypt_device *cd,
+ int token,
+ char **password,
+ size_t *password_len,
+ void *usrptr)
+{
+ int r;
+ json_object *jobj_server, *jobj_user, *jobj_path, *jobj_token;
+ ssh_session ssh;
+ password_cb_func pcb = usrptr; /* custom password callback */
+
+ jobj_token = get_token_jobj(cd, token);
+ json_object_object_get_ex(jobj_token, "ssh_server", &jobj_server);
+ json_object_object_get_ex(jobj_token, "ssh_user", &jobj_user);
+ json_object_object_get_ex(jobj_token, "ssh_path", &jobj_path);
+
+ ssh = ssh_session_init(cd, json_object_get_string(jobj_server),
+ json_object_get_string(jobj_user));
+ if (!ssh)
+ return -EINVAL;
+
+ r = _public_key_auth(cd, ssh);
+
+ /* try password method fallback. superficial example use case for an usrptr */
+ if (r != SSH_AUTH_SUCCESS && pcb) {
+ crypt_log(cd, CRYPT_LOG_DEBUG, "Trying password method instead.\n");
+ r = _password_auth(cd, ssh, pcb);
+ }
+
+ if (r == SSH_AUTH_SUCCESS)
+ r = download_remote_password(cd, ssh, json_object_get_string(jobj_path),
+ password, password_len);
+
+ ssh_session_close(ssh);
+
+ return r ? -EINVAL : r;
+}
+
+const crypt_token_handler SSHTEST_token = {
+ .name = "sshkeytest",
+ .open = SSHTEST_token_open,
+};
+
+static int token_add(const char *device, const char *server,
+ const char *user, const char *path)
+{
+ struct crypt_device *cd = NULL;
+ json_object *jobj = NULL, *jobj_keyslots;
+ int r;
+
+ r = crypt_token_register(&SSHTEST_token);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = crypt_init(&cd, device);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = crypt_load(cd, CRYPT_LUKS2, NULL);
+ if (r < 0) {
+ crypt_free(cd);
+ return EXIT_FAILURE;
+ }
+
+ jobj = json_object_new_object();
+ json_object_object_add(jobj, "type", json_object_new_string(SSHTEST_token.name)); /* mandatory */
+
+ jobj_keyslots = json_object_new_array();
+ json_object_array_add(jobj_keyslots, json_object_new_string("0")); /* assign to first keyslot only */
+ json_object_object_add(jobj, "keyslots", jobj_keyslots); /* mandatory array field (may be empty and assigned later */
+
+ /* custom metadata */
+ json_object_object_add(jobj, "ssh_server", json_object_new_string(server));
+ json_object_object_add(jobj, "ssh_user", json_object_new_string(user));
+ json_object_object_add(jobj, "ssh_path", json_object_new_string(path));
+
+ /* libcryptsetup API call */
+ r = crypt_token_json_set(cd, TOKEN_NUM, json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PLAIN));
+
+ crypt_free(cd);
+ json_object_put(jobj);
+
+ return EXIT_SUCCESS;
+}
+
+
+/* naive implementation of password prompt. Yes it will print out the password on input :) */
+static int ssh_password_callback(char **ssh_password)
+{
+ ssize_t i;
+ char *pass = malloc(512);
+
+ if (!pass)
+ return -ENOMEM;
+
+ fprintf(stdout, "Host asks for password:\n");
+
+ i = read(STDIN_FILENO, pass, 512);
+ if (i > 0) {
+ pass[i-1] = '\0';
+ i = 0;
+ } else if (i == 0) { /* EOF */
+ *pass = '\0';
+ i = -1;
+ }
+
+ if (!i)
+ *ssh_password = pass;
+ else
+ free(pass);
+
+ return i;
+}
+
+static int open_by_token(const char *device, const char *name)
+{
+ struct crypt_device *cd = NULL;
+ int r;
+
+ r = crypt_token_register(&SSHTEST_token);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = crypt_init(&cd, device);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = crypt_load(cd, CRYPT_LUKS2, NULL);
+ if (r < 0) {
+ crypt_free(cd);
+ return EXIT_FAILURE;
+ }
+
+ r = crypt_activate_by_token(cd, name, TOKEN_NUM, ssh_password_callback, 0);
+
+ crypt_free(cd);
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+static void keyslot_help(void)
+{
+ printf("Use parameters:\n add device server user path\n"
+ " open device name\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ crypt_set_debug_level(CRYPT_LOG_DEBUG);
+
+ /* Adding slot to device */
+ if (argc == 6 && !strcmp("add", argv[1]))
+ return token_add(argv[2], argv[3], argv[4], argv[5]);
+
+ /* Key check without activation */
+ if (argc == 3 && !strcmp("open", argv[1]))
+ return open_by_token(argv[2], NULL);
+
+ /* Key check with activation (requires root) */
+ if (argc == 4 && !strcmp("open", argv[1]))
+ return open_by_token(argv[2], argv[3]);
+
+ keyslot_help();
+ return 1;
+}
diff --git a/misc/luks2_keyslot_example/keyslot_test_remote_pass.c b/misc/luks2_keyslot_example/keyslot_test_remote_pass.c
new file mode 100644
index 0000000..5aa9500
--- /dev/null
+++ b/misc/luks2_keyslot_example/keyslot_test_remote_pass.c
@@ -0,0 +1,264 @@
+/*
+ * Example of LUKS2 token storing third party metadata (EXAMPLE)
+ *
+ * Copyright (C) 2016-2021 Milan Broz <gmazyland@gmail.com>
+ *
+ * Use:
+ * - generate LUKS device
+ * - store passphrase used in previous step remotely (single line w/o \n\r)
+ * - add new token using this example
+ * - activate device with passphrase recovered remotely using the example
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <json-c/json.h>
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
+#include "libcryptsetup.h"
+
+#define TOKEN_NUM 0
+#define TOKEN_TYPE "sshkeytest"
+
+#define PASSWORD_LENGTH 8192
+
+static json_object *get_token_jobj(struct crypt_device *cd, int token)
+{
+ const char *json_slot;
+
+ if (crypt_token_json_get(cd, token, &json_slot))
+ return NULL;
+
+ return json_tokener_parse(json_slot);
+}
+
+static int read_remote_passphrase(struct crypt_device *cd, const char *host,
+ const char *user, const char *path,
+ char *password, size_t password_size)
+{
+ ssh_session ssh = NULL;
+ sftp_session sftp = NULL;
+ sftp_file file = NULL;
+ ssh_key pkey = NULL;
+
+ int r, port = 22;
+
+ ssh = ssh_new();
+ if (!ssh)
+ return -EINVAL;
+
+ ssh_options_set(ssh, SSH_OPTIONS_HOST, host);
+ ssh_options_set(ssh, SSH_OPTIONS_USER, user);
+ ssh_options_set(ssh, SSH_OPTIONS_PORT, &port);
+
+ r = ssh_connect(ssh);
+ if (r != SSH_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Connection failed: ");
+ goto out;
+ }
+
+ r = ssh_is_server_known(ssh);
+ if (r != SSH_SERVER_KNOWN_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Server not known: ");
+ r = SSH_AUTH_ERROR;
+ goto out;
+ }
+
+ r = ssh_pki_import_privkey_file("/home/user/.ssh/id_rsa", NULL, NULL, NULL, &pkey);
+ if (r != SSH_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "error\n");
+ r = SSH_AUTH_ERROR;
+ goto out;
+ }
+
+ r = ssh_userauth_publickey(ssh, user, pkey);
+ /* or r = ssh_userauth_publickey_auto(ssh, user, NULL); */
+ if (r != SSH_AUTH_SUCCESS) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Public key authentication error: ");
+ goto out;
+ }
+
+ sftp = sftp_new(ssh);
+ if (!sftp) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Cannot create sftp session: ");
+ r = SSH_FX_FAILURE;
+ goto out;
+ }
+
+ r = sftp_init(sftp);
+ if (r != SSH_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Cannot init sftp session: ");
+ goto out;
+ }
+
+ file = sftp_open(sftp, path, O_RDONLY, 0);
+ if (!file) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Cannot create sftp session: ");
+ r = SSH_FX_FAILURE;
+ goto out;
+ }
+
+ r = sftp_read(file, password, password_size);
+ if (r < 1 || (size_t)r >= password_size) {
+ crypt_log(cd, CRYPT_LOG_ERROR, "Cannot read remote password: ");
+ r = SSH_FX_FAILURE;
+ goto out;
+ }
+
+ r = SSH_OK;
+out:
+ if (r != SSH_OK) {
+ crypt_log(cd, CRYPT_LOG_ERROR, ssh_get_error(ssh));
+ crypt_log(cd, CRYPT_LOG_ERROR, "\n");
+ }
+
+ if (pkey)
+ ssh_key_free(pkey);
+
+ if (file)
+ sftp_close(file);
+ if (sftp)
+ sftp_free(sftp);
+ ssh_disconnect(ssh);
+ ssh_free(ssh);
+ return r == SSH_OK ? 0 : -EINVAL;
+}
+
+static int token_add(const char *device, const char *server,
+ const char *user, const char *path)
+{
+ struct crypt_device *cd = NULL;
+ json_object *jobj = NULL, *jobj_keyslots;
+ int r;
+
+ r = crypt_init(&cd, device);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = crypt_load(cd, CRYPT_LUKS2, NULL);
+ if (r < 0) {
+ crypt_free(cd);
+ return EXIT_FAILURE;
+ }
+
+ jobj = json_object_new_object();
+
+ /* 'type' is mandatory field */
+ json_object_object_add(jobj, "type", json_object_new_string(TOKEN_TYPE));
+
+ /* 'keyslots' is mandatory field (may be empty) */
+ jobj_keyslots = json_object_new_array();
+ json_object_array_add(jobj_keyslots, json_object_new_string("0"));
+ json_object_array_add(jobj_keyslots, json_object_new_string("1"));
+ json_object_object_add(jobj, "keyslots", jobj_keyslots);
+
+ /* third party values */
+ json_object_object_add(jobj, "ssh_server", json_object_new_string(server));
+ json_object_object_add(jobj, "ssh_user", json_object_new_string(user));
+ json_object_object_add(jobj, "ssh_path", json_object_new_string(path));
+
+ r = crypt_token_json_set(cd, TOKEN_NUM, json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PLAIN));
+
+ crypt_free(cd);
+ json_object_put(jobj);
+
+ return EXIT_SUCCESS;
+}
+
+static int download_remote_password(struct crypt_device *cd, char *password, size_t password_len)
+{
+ json_object *jobj_server, *jobj_user, *jobj_path, *jobj_keyslot;
+
+ /* get token json object representation as string */
+ jobj_keyslot = get_token_jobj(cd, TOKEN_NUM);
+ if (!jobj_keyslot)
+ return -EINVAL;
+
+
+ /* extract third party metadata necessary to extract passphrase remotely */
+ json_object_object_get_ex(jobj_keyslot, "ssh_server", &jobj_server);
+ json_object_object_get_ex(jobj_keyslot, "ssh_user", &jobj_user);
+ json_object_object_get_ex(jobj_keyslot, "ssh_path", &jobj_path);
+
+ return read_remote_passphrase(cd, json_object_get_string(jobj_server),
+ json_object_get_string(jobj_user),
+ json_object_get_string(jobj_path),
+ password, password_len);
+}
+
+static int open_by_remote_password(const char *device, const char *name)
+{
+ char password[PASSWORD_LENGTH+1];
+ struct crypt_device *cd = NULL;
+ int r;
+
+ r = crypt_init(&cd, device);
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = crypt_load(cd, CRYPT_LUKS2, NULL);
+ if (r < 0) {
+ crypt_free(cd);
+ return EXIT_FAILURE;
+ }
+
+ /* custom routines to acquire password */
+ r = download_remote_password(cd, password, sizeof(password));
+ if (r < 0) {
+ crypt_free(cd);
+ return EXIT_FAILURE;
+ }
+
+ password[PASSWORD_LENGTH] = '\0';
+
+ /* open first genuine LUKS2 keyslot available provided the password matches */
+ /* for the sake of simplicity password is a string */
+ r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, password, strlen(password), 0);
+
+ crypt_free(cd);
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+static void keyslot_help(void)
+{
+ printf("Use parameters:\n add device server user path\n"
+ " open device name\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ crypt_set_debug_level(CRYPT_LOG_DEBUG);
+
+ /* Adding slot to device */
+ if (argc == 6 && !strcmp("add", argv[1]))
+ return token_add(argv[2], argv[3], argv[4], argv[5]);
+
+ /* Password check without activation */
+ if (argc == 3 && !strcmp("open", argv[1]))
+ return open_by_remote_password(argv[2], NULL);
+
+ /* Password check with activation (requires root) */
+ if (argc == 4 && !strcmp("open", argv[1]))
+ return open_by_remote_password(argv[2], argv[3]);
+
+ keyslot_help();
+ return 1;
+}