summaryrefslogtreecommitdiffstats
path: root/debian/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'debian/scripts')
-rw-r--r--debian/scripts/cryptdisks_start63
-rw-r--r--debian/scripts/cryptdisks_stop38
-rw-r--r--debian/scripts/decrypt_derived32
-rw-r--r--debian/scripts/decrypt_gnupg26
-rw-r--r--debian/scripts/decrypt_gnupg-sc44
-rw-r--r--debian/scripts/decrypt_keyctl55
-rw-r--r--debian/scripts/decrypt_opensc46
-rw-r--r--debian/scripts/decrypt_ssl17
-rw-r--r--debian/scripts/gen-ssl-key22
-rw-r--r--debian/scripts/luksformat133
-rw-r--r--debian/scripts/passdev.c286
-rw-r--r--debian/scripts/po/Makefile39
-rw-r--r--debian/scripts/po/de.po76
-rw-r--r--debian/scripts/po/luksformat.pot69
-rw-r--r--debian/scripts/suspend/cryptsetup-suspend-wrapper320
-rw-r--r--debian/scripts/suspend/cryptsetup-suspend.c225
-rw-r--r--debian/scripts/suspend/cryptsetup-suspend.shutdown3
-rw-r--r--debian/scripts/suspend/suspend.conf10
-rw-r--r--debian/scripts/suspend/systemd/cryptsetup-suspend.conf12
19 files changed, 1516 insertions, 0 deletions
diff --git a/debian/scripts/cryptdisks_start b/debian/scripts/cryptdisks_start
new file mode 100644
index 0000000..623423f
--- /dev/null
+++ b/debian/scripts/cryptdisks_start
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+# cryptdisks_start - wrapper around cryptsetup which parses
+# /etc/crypttab, just like mount parses /etc/fstab.
+
+# Initial code and (c) 2007 Jon Dowland <jon@alcopop.org>
+# License: GNU General Public License, v2 or any later
+# (https://www.gnu.org/copyleft/gpl.html)
+
+set -e
+
+. /lib/cryptsetup/cryptdisks-functions
+
+INITSTATE="manual"
+DEFAULT_LOUD="yes"
+FORCE_START="yes"
+
+usage() {
+ local rv="${1:-1}"
+ echo "Usage: $0 [-r|--readonly] <name> [.. <name>]" >&2
+ echo >&2
+ echo "reads $TABFILE and starts the mapping corresponding to <name>" >&2
+ exit $rv
+}
+
+CRYPTTAB_EXTRA_OPTIONS=
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -r|--readonly) CRYPTTAB_EXTRA_OPTIONS="${CRYPTTAB_EXTRA_OPTIONS:+$CRYPTTAB_EXTRA_OPTIONS,}readonly";;
+ -h|--help|-\?) usage 0;;
+ --) shift; break;;
+ -*) echo "Error: unknown option '$1'" >&2; usage 1;;
+ *) break;;
+ esac
+ shift
+done
+[ $# -gt 0 ] || usage 1
+
+if [ $(id -u) -ne 0 ]; then
+ log_warning_msg "$0 needs root privileges"
+ exit 1
+fi
+
+log_action_begin_msg "Starting crypto disk"
+mount_fs
+
+rv=0
+for name in "$@"; do
+ if ! crypttab_find_entry --quiet "$name"; then
+ device_msg "$name" "failed, not found in crypttab"
+ rv=1
+ else
+ if [ -n "$CRYPTTAB_EXTRA_OPTIONS" ]; then
+ CRYPTTAB_OPTIONS="$CRYPTTAB_OPTIONS,$CRYPTTAB_EXTRA_OPTIONS"
+ _CRYPTTAB_OPTIONS="$_CRYPTTAB_OPTIONS,$CRYPTTAB_EXTRA_OPTIONS"
+ fi
+ setup_mapping || rv=$?
+ fi
+done
+umount_fs
+
+log_action_end_msg $rv
+exit $rv
diff --git a/debian/scripts/cryptdisks_stop b/debian/scripts/cryptdisks_stop
new file mode 100644
index 0000000..ea0faaf
--- /dev/null
+++ b/debian/scripts/cryptdisks_stop
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# cryptdisks_stop - wrapper around cryptsetup which parses
+# /etc/crypttab, just like mount parses /etc/fstab.
+
+# Initial code stolen from cryptdisks_start by Jon Dowland <jon@alcopop.org>
+# Copyright (C) 2008 by Jonas Meurer <jonas@freesources.org>
+# License: GNU General Public License, v2 or any later
+# (https://www.gnu.org/copyleft/gpl.html)
+
+set -e
+
+if [ $# -lt 1 ]; then
+ echo "usage: $0 <name>" >&2
+ echo >&2
+ echo "reads /etc/crypttab and stops the mapping corresponding to <name>" >&2
+ exit 1
+fi
+
+. /lib/cryptsetup/cryptdisks-functions
+
+INITSTATE="manual"
+DEFAULT_LOUD="yes"
+
+if [ $(id -u) -ne 0 ]; then
+ log_warning_msg "$0 needs root privileges"
+ exit 1
+fi
+
+log_action_begin_msg "Stopping crypto disk"
+
+rv=0
+for name in "$@"; do
+ remove_mapping "$name" || rv=$?
+done
+
+log_action_end_msg $rv
+exit $rv
diff --git a/debian/scripts/decrypt_derived b/debian/scripts/decrypt_derived
new file mode 100644
index 0000000..0e1e418
--- /dev/null
+++ b/debian/scripts/decrypt_derived
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# WARNING: If you use the decrypt_derived keyscript for devices with
+# persistent data (i.e. not swap or temp devices), then you will lose
+# access to that data permanently if something damages the LUKS header
+# of the LUKS device you derive from. The same applies if you luksFormat
+# the device, even if you use the same passphrase(s). A LUKS header
+# backup, or better a backup of the data on the derived device may be
+# a good idea. See the Cryptsetup FAQ on how to do this right.
+
+if [ -z "$1" ]; then
+ echo "$0: must be executed with a crypto device as argument" >&2
+ exit 1
+fi
+
+unset -v keys count
+keys="$(dmsetup table --target crypt --showkeys -- "$1" 2>/dev/null | cut -s -d' ' -f5)"
+count="$(printf '%s' "$keys" | wc -l)"
+
+if [ -n "$keys" ] && [ $count -le 1 ]; then
+ if [ "${keys#:}" = "$keys" ]; then
+ printf '%s' "$keys"
+ exit 0
+ else
+ echo "$0: device $1 uses the kernel keyring" >&2
+ fi
+elif [ $count -eq 0 ]; then
+ echo "$0: device $1 doesn't exist or isn't a crypto device" >&2
+else
+ echo "$0: more than one device match" >&2
+fi
+exit 1
diff --git a/debian/scripts/decrypt_gnupg b/debian/scripts/decrypt_gnupg
new file mode 100644
index 0000000..18ab575
--- /dev/null
+++ b/debian/scripts/decrypt_gnupg
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+decrypt_gpg () {
+ echo "Performing GPG symmetric decryption ..." >&2
+ if ! /lib/cryptsetup/askpass "Enter passphrase for key $1: " | \
+ /usr/bin/gpg -q --batch --no-options \
+ --no-random-seed-file --no-default-keyring \
+ --keyring /dev/null --secret-keyring /dev/null \
+ --trustdb-name /dev/null --passphrase-fd 0 --decrypt -- "$1"; then
+ return 1
+ fi
+ return 0
+}
+
+if [ ! -x /usr/bin/gpg ]; then
+ echo "$0: /usr/bin/gpg is not available" >&2
+ exit 1
+fi
+
+if [ -z "$1" ]; then
+ echo "$0: missing key as argument" >&2
+ exit 1
+fi
+
+decrypt_gpg "$1"
+exit $?
diff --git a/debian/scripts/decrypt_gnupg-sc b/debian/scripts/decrypt_gnupg-sc
new file mode 100644
index 0000000..84eb62c
--- /dev/null
+++ b/debian/scripts/decrypt_gnupg-sc
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+if [ -d "/cryptroot/gnupghome" ]; then
+ export GNUPGHOME="/cryptroot/gnupghome"
+fi
+
+run_gpg() {
+ gpg --no-options --trust-model=always "$@"
+}
+decrypt_gpg () {
+ local console _
+ if ! GPG_TTY="$(tty)"; then
+ read console _ </proc/consoles
+ GPG_TTY="/dev/$console"
+ fi
+ export GPG_TTY
+
+ if ! run_gpg --decrypt -- "$1"; then
+ return 1
+ fi
+ return 0
+}
+
+# `gpg-connect-agent LEARN /bye` is another (lighter) way, but it's
+# harder to retrieve the return code
+if ! run_gpg --batch --quiet --no-tty --card-status >/dev/null; then
+ echo "Please insert OpenPGP SmartCard..." >&2
+ until run_gpg --batch --quiet --no-tty --card-status; do
+ sleep 1
+ done >/dev/null 2>&1
+fi
+
+if [ ! -x /usr/bin/gpg ]; then
+ echo "$0: /usr/bin/gpg is not available" >&2
+ exit 1
+fi
+
+if [ -z "$1" ] || [ ! -f "$1" ]; then
+ echo "$0: missing key as argument" >&2
+ exit 1
+fi
+
+decrypt_gpg "$1"
+exit $?
diff --git a/debian/scripts/decrypt_keyctl b/debian/scripts/decrypt_keyctl
new file mode 100644
index 0000000..6032db0
--- /dev/null
+++ b/debian/scripts/decrypt_keyctl
@@ -0,0 +1,55 @@
+#!/bin/sh
+# decrypt_keyctl - to use in /etc/crypttab as keyscript
+# Allows to cache passwords for cryptdevices for 60s
+# The same password is used for for cryptdevices with the same identifier.
+# The keyfile parameter, which is the third field from /etc/crypttab, is
+# used as identifier in this keyscript.
+#
+# sample crypttab entries:
+# test1 /dev/sda1 test_pw luks,keyscript=decrypt_keyctl
+# test2 /dev/sda2 test_pw luks,keyscript=decrypt_keyctl
+# test3 /dev/sda3 test_other_pw luks,keyscript=decrypt_keyctl
+#
+# test1 and test2 have the same identifier thus test2 does not need a password
+# typed in manually
+
+die()
+{
+ echo "$@" >&2
+ exit 1
+}
+
+if [ -z "${CRYPTTAB_KEY:-}" ] || [ "$CRYPTTAB_KEY" = "none" ]; then
+ # store the passphrase in the key name used by systemd-ask-password
+ ID_="cryptsetup"
+else
+ # the keyfile given from crypttab is used as identifier in the keyring
+ # including the prefix "cryptsetup:"
+ ID_="cryptsetup:$CRYPTTAB_KEY"
+fi
+TIMEOUT_='60'
+ASKPASS_='/lib/cryptsetup/askpass'
+PROMPT_="Caching passphrase for ${CRYPTTAB_NAME}: "
+
+
+if ! KID_="$(keyctl search @u user "$ID_" 2>/dev/null)" || \
+ [ -z "$KID_" ] || [ "$CRYPTTAB_TRIED" -gt 0 ]; then
+ # key not found or wrong, ask the user
+ KEY_="$($ASKPASS_ "$PROMPT_")" || die "Error executing $ASKPASS_"
+ if [ -n "$KID_" ]; then
+ # I have cached wrong password and now i may use either `keyctl update`
+ # to update $KID_ or just unlink old key, and add new. With `update` i
+ # may hit "Key has expired", though. So i'll go "unlink and add" way.
+ keyctl unlink "$KID_" @u
+ KID_=""
+ fi
+ KID_="$(printf "%s" "$KEY_" | keyctl padd user "$ID_" @u)"
+ [ -n "$KID_" ] || die "Error adding passphrase to kernel keyring"
+ if ! keyctl timeout "$KID_" "$TIMEOUT_"; then
+ keyctl unlink "$KID_" @u
+ die "Error setting timeout on key ($KID_), removing"
+ fi
+else
+ echo "Using cached passphrase for ${CRYPTTAB_NAME}." >&2
+fi
+keyctl pipe "$KID_"
diff --git a/debian/scripts/decrypt_opensc b/debian/scripts/decrypt_opensc
new file mode 100644
index 0000000..b06fc98
--- /dev/null
+++ b/debian/scripts/decrypt_opensc
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# Why not use "openct-tool rwait" instead of polling opensc-tool exit status?
+# Well openct daemon has to be running which interferes with pcscd since both
+# implement reader drivers, my particular CCID reader (SCM SCR331-LC1) doesn't
+# work with the CCID driver in openct, however it does work with pcscd.
+
+# Why not use "opensc-tool --wait" instead of polling opensc-tool exit status?
+# Although opensc-tool --help reports that there is a --wait option, it doesn't
+# seem to be implemented.
+
+check_card() {
+ cardfound=0
+
+ if /usr/bin/opensc-tool -n >/dev/null 2>&1; then
+ cardfound=1
+ fi
+}
+
+wait_card() {
+ check_card
+ if [ $cardfound = 0 ] ; then
+ echo "Waiting for Smart Card..." >&2
+ tries=0
+ while [ $cardfound = 0 ] && [ $tries -lt 60 ] ; do
+ sleep 1
+ check_card
+ tries=$(($tries + 1))
+ done
+ if [ $cardfound = 0 ] ; then
+ echo 'Failed to find Smart Card card!' >&2
+ exit 1
+ fi
+ fi
+}
+
+wait_card
+if [ -x /bin/plymouth ] && plymouth --ping; then
+ # Get pin number from plymouth
+ /usr/bin/pkcs15-crypt --decipher --input "$1" --pkcs1 --raw \
+ --pin "$(plymouth ask-for-password --prompt "Enter pin for $CRYPTTAB_NAME: ")"
+else
+ # Get pin number from console
+ /usr/bin/pkcs15-crypt --decipher --input "$1" --pkcs1 --raw </dev/console 2>/dev/console
+fi
+exit $?
diff --git a/debian/scripts/decrypt_ssl b/debian/scripts/decrypt_ssl
new file mode 100644
index 0000000..6664001
--- /dev/null
+++ b/debian/scripts/decrypt_ssl
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# Script to decrypt the key which is encrypted with openssl.
+# See /usr/share/doc/cryptsetup/examples/gen-ssl-key to create such a key.
+#
+
+decrypt_ssl () {
+ echo "" >&2
+ echo "Decrypting ssl key $1..." >&2
+ if ! /usr/bin/openssl enc -aes-256-cbc -d -salt -in "$1" 2>/dev/null; then
+ return 1
+ fi
+ return 0
+}
+
+decrypt_ssl "$1"
+exit $?
diff --git a/debian/scripts/gen-ssl-key b/debian/scripts/gen-ssl-key
new file mode 100644
index 0000000..70a6fb3
--- /dev/null
+++ b/debian/scripts/gen-ssl-key
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# script to generate a keyfile that is encrypted with openssl
+#
+# Written 2005 by Markus Nass <generalstone@gmx.net>
+# Improved 2006 by Jonas Meurer <jonas@freesources.org>
+# Further improved 2006 by Markus Nass <generalstone@gmx.net>
+
+usage() {
+ echo "Usage: $0 <key>"
+ exit 1
+}
+
+if [ -z "${1-}" ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
+ usage
+fi
+
+if [ -x /usr/bin/openssl ]; then
+ dd if=/dev/random bs=1c count=256 | openssl enc -aes-256-cbc -e -salt >"$1"
+else
+ echo "/usr/bin/openssl is not available" && exit 1
+fi
diff --git a/debian/scripts/luksformat b/debian/scripts/luksformat
new file mode 100644
index 0000000..ae17f79
--- /dev/null
+++ b/debian/scripts/luksformat
@@ -0,0 +1,133 @@
+#!/usr/bin/perl -w
+
+# luksformat - wrapper around LUKS-capable cryptsetup and mkfs for easy
+# creation of an encrypted device.
+#
+# (C) 2005 Canonical Ltd.
+# Author: Martin Pitt <martin.pitt@ubuntu.com>
+# License: GNU General Public License, v2 or any later
+# (https://www.gnu.org/copyleft/gpl.html)
+
+use Getopt::Long qw(:config pass_through);
+
+BEGIN {
+ eval 'use Locale::gettext';
+ if ($@) {
+ *gettext = sub { shift };
+ *textdomain = sub { "" };
+ *LC_MESSAGES = sub { 5 };
+ }
+ eval {
+ require POSIX;
+ import POSIX qw(setlocale);
+ };
+ if ($@) {
+ *setlocale = sub { return 1 };
+ }
+}
+
+setlocale(LC_MESSAGES, "");
+textdomain("luksformat");
+
+if ($> != 0) {
+ print STDERR gettext("This program needs to be started as root\n");
+ exit 1;
+}
+
+sub usage() {
+ print gettext("luksformat - Create and format an encrypted LUKS device
+Usage: luksformat [-t <file system>] <device> [ mkfs options ]\n\n");
+ exit 1;
+}
+
+# default file system
+$fs = 'vfat';
+exit 1 unless GetOptions ('t|type=s' => \$fs);
+
+GetOptions ('help', \$help);
+if (($#ARGV < 0) || ($help)) {
+ usage();
+}
+
+$device = shift(@ARGV);
+
+open(MOUNTS, "/proc/mounts");
+while (<MOUNTS>) {
+ die sprintf(gettext("Error: device mounted: %s\n"), $device) if (/\Q$device\E/)
+}
+
+if (-x "/usr/sbin/mkfs.$fs") {
+ $mkfs = "/usr/sbin/mkfs.$fs";
+}
+elsif (-x "/usr/bin/mkfs.$fs") {
+ $mkfs = "/usr/bin/mkfs.$fs";
+}
+elsif (-x "/sbin/mkfs.$fs") {
+ $mkfs = "/sbin/mkfs.$fs";
+}
+elsif (-x "/bin/mkfs.$fs") {
+ $mkfs = "/bin/mkfs.$fs";
+}
+else {
+ printf STDERR (gettext("Error: invalid file system: %s\n"), $fs);
+ exit 1;
+}
+
+# generate temporary mapped device name which is not yet used
+$name = "";
+for ($i = 1; $i < 100; $i++) {
+ if (! -e "/dev/mapper/luksformat$i") {
+ $name = "luksformat$i";
+ last;
+ }
+}
+
+$name or die sprintf(gettext("Error: could not generate temporary mapped device name"));
+
+# we do not need to be overly concerned with race conditions here, cryptsetup
+# will just fail if the name already exists now.
+printf (gettext("Creating encrypted device on %s...\n"), $device);
+if ((system 'cryptsetup', 'luksFormat', $device)) {
+ die sprintf(gettext("Could not create LUKS device %s"), $device);
+}
+
+print gettext("Please enter your passphrase again to verify it\n");
+if ((system 'cryptsetup', 'open', '--type', 'luks', $device, $name) != 0) {
+ print STDERR gettext("The passphrases you entered were not identical\n");
+ exit 1;
+}
+
+$result = system $mkfs, "/dev/mapper/$name", @ARGV;
+print "\n";
+system 'udevadm', 'settle', '--timeout=30';
+system 'cryptsetup', 'luksClose', $name;
+
+die sprintf(gettext("Could not format device with file system %s"), $fs) if $result;
+
+__END__
+
+=head1 NAME
+
+luksformat - Create and format an encrypted LUKS device
+
+=head1 SYNOPSIS
+
+B<luksformat> [B<-t> I<fstype>] I<device> [ mkfs options ]
+
+=head1 DESCRIPTION
+
+B<luksformat> is a wrapper around B<cryptsetup> and B<mkfs> which provides an
+easy interface for creating an encrypted device that follows the LUKS standard
+and for putting a file system onto the encrypted device.
+
+The default file system is B<vfat> since that is most commonly used on
+removable devices. However, you can specify any available file system with the
+B<-t> option.
+
+=head1 SEE ALSO
+
+L<cryptsetup(8)>, L<mkfs(8)>
+
+=head1 AUTHOR
+
+This program was written by Martin Pitt <martin.pitt@ubuntu.com>.
diff --git a/debian/scripts/passdev.c b/debian/scripts/passdev.c
new file mode 100644
index 0000000..845ccae
--- /dev/null
+++ b/debian/scripts/passdev.c
@@ -0,0 +1,286 @@
+/*
+ * passdev.c - waits for a given device to appear, mounts it and reads a
+ * key from it which is piped to stdout.
+ *
+ * Copyright (C) 2008 David Härdeman <david@hardeman.nu>
+ *
+ * This package 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 package 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 package; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#define _DEFAULT_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+
+static bool do_debug = false;
+
+static void
+debug(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!do_debug)
+ return;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+static bool
+do_mount(const char *device, const char *dir)
+{
+ pid_t pid;
+ int status;
+ char *fstypes[] = { "ext4", "ext3", "ext2", "vfat", "btrfs", "reiserfs", "xfs", "jfs", "ntfs", "iso9660", "udf" };
+ int fsindex;
+
+ if (!device || !dir)
+ return false;
+
+ for (fsindex = 0;
+ fsindex < (sizeof(fstypes) / sizeof(fstypes[0]));
+ fsindex++)
+ {
+ pid = fork();
+ if (pid < 0) {
+ /* Error */
+ return false;
+ } else if (pid > 0) {
+ /* We're in the parent process */
+ do {
+ waitpid(pid, &status, 0);
+ } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+ if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
+ return true;
+
+ /* Let's try another fstype */
+ continue;
+ } else {
+ /* We're in the child process */
+ debug("Mounting %s at %s\n", device, dir);
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ open("/dev/null", O_RDONLY, 0);
+ open("/dev/null", O_WRONLY, 0);
+ open("/dev/null", O_WRONLY, 0);
+ execl("/bin/mount", "/bin/mount", "-n", "-t",
+ fstypes[fsindex],
+ /*"ext4,ext3,ext2,vfat,btrfs,reiserfs,xfs,jfs,ntfs,iso9660,udf",*/
+ "-o", "noatime,nodiratime,nodev,noexec,nosuid,ro",
+ device, dir, (char *)NULL);
+
+ /* If execl works, we won't end up here */
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* We've tried all fstypes with no luck */
+ return false;
+}
+
+int
+main(int argc, char **argv, char **envp)
+{
+ char *debugval;
+ char *devpath;
+ char *filepath;
+ struct stat st;
+ char *tmppath;
+ char tpath[] = "/tmp/passdev.XXXXXX";
+ char *keypath;
+ int fd;
+ size_t toread;
+ size_t bytesread;
+ char *keybuffer;
+ size_t towrite;
+ size_t byteswritten;
+ ssize_t bytes;
+ char *to;
+ int timeout = 0;
+ bool do_timeout = false;
+
+ /* We only take one argument */
+ if (argc != 2) {
+ fprintf(stderr, "Incorrect number of arguments\n");
+ goto error;
+ }
+
+ /* If DEBUG=1 is in the environment, enable debug messages */
+ debugval = getenv("DEBUG");
+ if (debugval && atoi(debugval) > 0)
+ do_debug = true;
+
+ /* Split string into device and path (and timeout) */
+ devpath = argv[1];
+ filepath = strchr(devpath, ':');
+ if (!filepath || !(*filepath) || !(*(filepath + 1))) {
+ fprintf(stderr, "Invalid key path\n");
+ goto error;
+ }
+ *filepath = '\0';
+ filepath++;
+ to = strchr(filepath, ':');
+ if (to && (*to) && (*(to + 1))) {
+ *to = '\0';
+ to++;
+ timeout = atoi(to);
+ if (timeout > 0)
+ do_timeout = true;
+ }
+ debug("Path is %p and filepath is %p\n", devpath, filepath);
+ if (do_timeout)
+ debug("Timeout is %i\n",timeout);
+
+ /* Wait until device is available */
+ if (access(devpath, F_OK)) {
+ debug("Waiting for %s\n", devpath);
+ while(access(devpath, F_OK)) {
+ sleep(1);
+ if (do_timeout) {
+ if (timeout <= 0)
+ break;
+ timeout--;
+ }
+ }
+ }
+
+ /* Make sure device is a blockdev */
+ if (stat(devpath, &st)) {
+ fprintf(stderr, "Unable to stat %s\n", devpath);
+ goto error;
+ } else if (!S_ISBLK(st.st_mode)) {
+ fprintf(stderr, "%s is no block device\n", devpath);
+ goto error;
+ }
+
+ /* Create a tmp dir where we mount the device */
+ tmppath = mkdtemp(tpath);
+ if (!tmppath) {
+ fprintf(stderr, "Failed to create temporary directory\n");
+ goto error;
+ }
+
+ /* Ok, mount it */
+ if (!do_mount(devpath, tmppath)) {
+ fprintf(stderr, "Failed to mount %s\n", devpath);
+ goto error_rmdir;
+ }
+
+ /* Generate the full path to the keyfile */
+ keypath = malloc(strlen(tmppath) + 1 + strlen(filepath) + 1);
+ if (!keypath) {
+ fprintf(stderr, "Failed to allocate memory\n");
+ goto error_umount;
+ }
+ sprintf(keypath, "%s/%s", tmppath, filepath);
+
+ /* Check that the keyfile exists */
+ if (access(keypath, F_OK)) {
+ fprintf(stderr, "Keyfile doesn't exist\n");
+ goto error_free;
+ }
+
+ /* Get the size of the keyfile */
+ if (stat(keypath, &st)) {
+ fprintf(stderr, "Unable to stat keyfile\n");
+ goto error_free;
+ }
+
+ /* Check the size of the keyfile */
+ if (st.st_size < 0) {
+ fprintf(stderr, "Invalid keyfile size\n");
+ goto error_free;
+ }
+ toread = (size_t)st.st_size;
+
+ /* Open the keyfile */
+ if ((fd = open(keypath, O_RDONLY)) < 0) {
+ fprintf(stderr, "Failed to open keyfile\n");
+ goto error_free;
+ }
+
+ /* Allocate a buffer for the keyfile contents */
+ keybuffer = malloc(toread);
+ if (!keybuffer) {
+ fprintf(stderr, "Failed to allocate memory\n");
+ goto error_close;
+ exit(EXIT_FAILURE);
+ }
+
+ /* Read the keyfile */
+ bytesread = 0;
+ while (bytesread < toread) {
+ bytes = read(fd, keybuffer + bytesread, toread - bytesread);
+ if (bytes <= 0) {
+ fprintf(stderr, "Failed to read entire key\n");
+ goto error_keybuffer;
+ }
+ bytesread += bytes;
+ }
+
+ /* Clean up */
+ close(fd);
+ free(keypath);
+ umount(tmppath);
+ rmdir(tmppath);
+
+ /* Write result */
+ byteswritten = 0;
+ towrite = toread;
+ while (byteswritten < towrite) {
+ bytes = write(STDOUT_FILENO, keybuffer + byteswritten,
+ towrite - byteswritten);
+ if (bytes <= 0) {
+ fprintf(stderr, "Failed to write entire key\n");
+ memset(keybuffer, 0, toread);
+ free(keybuffer);
+ goto error;
+ }
+ byteswritten += bytes;
+ }
+
+ /* Clean up */
+ memset(keybuffer, 0, toread);
+ free(keybuffer);
+
+ /* Done */
+ exit(EXIT_SUCCESS);
+
+ /* Error handling */
+error_keybuffer:
+ memset(keybuffer, 0, toread);
+ free(keybuffer);
+error_close:
+ close(fd);
+error_free:
+ free(keypath);
+error_umount:
+ umount(tmppath);
+error_rmdir:
+ rmdir(tmppath);
+error:
+ exit(EXIT_FAILURE);
+}
+
diff --git a/debian/scripts/po/Makefile b/debian/scripts/po/Makefile
new file mode 100644
index 0000000..9eb8acf
--- /dev/null
+++ b/debian/scripts/po/Makefile
@@ -0,0 +1,39 @@
+XGETTEXT = xgettext
+MSGFMT = msgfmt
+MSGMERGE = msgmerge
+
+LOCALEDIR = /usr/share/locale
+
+.SUFFIXES: .po .mo .pot
+
+%.mo: %.po
+ $(MSGFMT) -o $@ $<
+
+PO = $(wildcard *.po)
+LANG = $(basename $(PO))
+MO = $(addsuffix .mo,$(LANG))
+SOURCES = ../luksformat
+
+all: update $(MO)
+update: luksformat.pot
+ -@for po in $(PO); do \
+ echo -n "Updating $$po"; \
+ $(MSGMERGE) -U $$po luksformat.pot; \
+ done;
+
+luksformat.pot: $(SOURCES)
+ $(XGETTEXT) -c -L Perl -kgtx \
+ --msgid-bugs-address=pkg-cryptsetup-devel@alioth-lists.debian.net \
+ -o $@ $(SOURCES)
+
+install: all
+ for i in $(MO) ; do \
+ t=$(DESTDIR)/$(LOCALEDIR)/`basename $$i .mo`/LC_MESSAGES ;\
+ install -d $$t ;\
+ install -m 644 $$i $$t/luksformat.mo ;\
+ done
+
+clean:
+ $(RM) $(MO) *~
+
+.PHONY: update
diff --git a/debian/scripts/po/de.po b/debian/scripts/po/de.po
new file mode 100644
index 0000000..76c7f2f
--- /dev/null
+++ b/debian/scripts/po/de.po
@@ -0,0 +1,76 @@
+# German translations for cryptsetup package
+# German messages for luksformat in cryptsetup.
+# Copyright (C) 2011 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the cryptsetup package.
+# Jonas Meurer <jonas@freesources.org>, 2011.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: cryptsetup 2:1.3.0-1\n"
+"Report-Msgid-Bugs-To: pkg-cryptsetup-devel@alioth-lists.debian.net\n"
+"POT-Creation-Date: 2015-12-09 13:09+0100\n"
+"PO-Revision-Date: 2011-03-08 19:40+0100\n"
+"Last-Translator: Jonas Meurer <jonas@freesources.org>\n"
+"Language-Team: German\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../luksformat:33
+msgid "This program needs to be started as root\n"
+msgstr "Dieses Programm muss als Benutzer root gestartet werden\n"
+
+#: ../luksformat:38
+msgid ""
+"luksformat - Create and format an encrypted LUKS device\n"
+"Usage: luksformat [-t <file system>] <device> [ mkfs options ]\n"
+"\n"
+msgstr ""
+"luksformat - LUKS-verschlüsselte Partition erstellen und formatieren\n"
+"Verwendung: luksformat [-t <Dateisystem>] <Partition> [ mkfs Optionen ]\n"
+"\n"
+
+#: ../luksformat:56
+#, perl-format
+msgid "Error: device mounted: %s\n"
+msgstr "Fehler: Partition ist eingebunden: %s\n"
+
+#: ../luksformat:72
+#, perl-format
+msgid "Error: invalid file system: %s\n"
+msgstr "Fehler: Ungültiges Dateisystem: %s\n"
+
+#: ../luksformat:85
+#, perl-format
+msgid "Error: could not generate temporary mapped device name"
+msgstr "Fehler: Erstellen einer temporären Partition schlug fehl"
+
+#. we do not need to be overly concerned with race conditions here, cryptsetup
+#. will just fail if the name already exists now.
+#: ../luksformat:89
+#, perl-format
+msgid "Creating encrypted device on %s...\n"
+msgstr "Erstelle verschlüsselte Partition auf %s...\n"
+
+#: ../luksformat:91
+#, perl-format
+msgid "Could not create LUKS device %s"
+msgstr "Erstellen der LUKS-Partition %s schlug fehl"
+
+#: ../luksformat:94
+msgid "Please enter your passphrase again to verify it\n"
+msgstr "Bitte zum verifizieren das Passwort erneut eingeben\n"
+
+#: ../luksformat:96
+msgid "The passphrases you entered were not identical\n"
+msgstr "Die eingegebenen Passwörter waren nicht identisch\n"
+
+#: ../luksformat:105
+#, perl-format
+msgid "Could not format device with file system %s"
+msgstr "Formatieren der Partition mit dem Dateisystem %s schlug fehl"
+
+#~ msgid "%s: %s"
+#~ msgstr "%s: %s"
diff --git a/debian/scripts/po/luksformat.pot b/debian/scripts/po/luksformat.pot
new file mode 100644
index 0000000..f6c1e56
--- /dev/null
+++ b/debian/scripts/po/luksformat.pot
@@ -0,0 +1,69 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: pkg-cryptsetup-devel@alioth-lists.debian.net\n"
+"POT-Creation-Date: 2015-12-09 13:09+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../luksformat:33
+msgid "This program needs to be started as root\n"
+msgstr ""
+
+#: ../luksformat:38
+msgid ""
+"luksformat - Create and format an encrypted LUKS device\n"
+"Usage: luksformat [-t <file system>] <device> [ mkfs options ]\n"
+"\n"
+msgstr ""
+
+#: ../luksformat:56
+#, perl-format
+msgid "Error: device mounted: %s\n"
+msgstr ""
+
+#: ../luksformat:72
+#, perl-format
+msgid "Error: invalid file system: %s\n"
+msgstr ""
+
+#: ../luksformat:85
+#, perl-format
+msgid "Error: could not generate temporary mapped device name"
+msgstr ""
+
+#. we do not need to be overly concerned with race conditions here, cryptsetup
+#. will just fail if the name already exists now.
+#: ../luksformat:89
+#, perl-format
+msgid "Creating encrypted device on %s...\n"
+msgstr ""
+
+#: ../luksformat:91
+#, perl-format
+msgid "Could not create LUKS device %s"
+msgstr ""
+
+#: ../luksformat:94
+msgid "Please enter your passphrase again to verify it\n"
+msgstr ""
+
+#: ../luksformat:96
+msgid "The passphrases you entered were not identical\n"
+msgstr ""
+
+#: ../luksformat:105
+#, perl-format
+msgid "Could not format device with file system %s"
+msgstr ""
diff --git a/debian/scripts/suspend/cryptsetup-suspend-wrapper b/debian/scripts/suspend/cryptsetup-suspend-wrapper
new file mode 100644
index 0000000..953196c
--- /dev/null
+++ b/debian/scripts/suspend/cryptsetup-suspend-wrapper
@@ -0,0 +1,320 @@
+#!/bin/sh
+
+# Wrapper for cryptsetup-suspend(7)
+#
+# Copyright © 2019-2020 Tim <tim@systemli.org>
+# © 2019-2020 Jonas Meurer <jonas@freesources.org>
+# © 2020-2022 Guilhem Moulin <guilhem@debian.org>
+#
+# 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, see <http://www.gnu.org/licenses/>.
+
+set -ue
+PATH="/usr/sbin:/usr/bin:/sbin:/bin"
+export PATH
+
+# import cryptsetup shell functions
+[ -f /lib/cryptsetup/functions ] || return 0
+. /lib/cryptsetup/functions
+
+INITRAMFS_MNT="/run/cryptsetup/cryptsetup-suspend-initramfs"
+SYSTEM_SLEEP_PATH="/lib/systemd/system-sleep"
+CONFIG_FILE="/etc/cryptsetup/suspend.conf"
+unset -v INITRAMFS_DIR
+
+read_config() {
+ # define defaults
+ export UNLOCK_SESSIONS="false"
+ export KEEP_INITRAMFS="false"
+
+ # read config file if it exists
+ # shellcheck source=/etc/cryptsetup/suspend.conf
+ [ -f "$CONFIG_FILE" ] && . "$CONFIG_FILE" || true
+}
+
+# run_dir ARGS...
+# Run all executable scripts in directory SYSTEM_SLEEP_PATH with arguments ARGS
+# mimic systemd behavior
+run_dir() {
+ [ -d "$SYSTEM_SLEEP_PATH" ] || return 0
+ find "$SYSTEM_SLEEP_PATH" -type f -executable -execdir {} "$@" \;
+}
+
+log_error() {
+ # arg1 should be message
+ echo "Error: $1" | systemd-cat -t cryptsetup-suspend -p err
+ echo "Error: $1" >&2
+}
+
+mount_initramfs() {
+ local k v u IFS MemAvailable=0 SwapFree=0 new="n"
+ # update-initramfs(8) hardcodes /boot also: there is a `-b bootdir`
+ # option but no config file to put it to
+ local INITRAMFS="/boot/initrd.img-$(uname -r)" p
+ if [ ! -f "$INITRAMFS" ]; then
+ log_error "No initramfs found at $INITRAMFS"
+ exit 1
+ fi
+
+ if [ -d "$INITRAMFS_MNT" ] && [ ! "$INITRAMFS" -ot "$INITRAMFS_MNT" ]; then
+ # need to unpack again: initramfs is newer than what we unpacked earlier
+ if mountpoint -q "$INITRAMFS_MNT"; then
+ umount "$INITRAMFS_MNT"
+ fi
+ rmdir "$INITRAMFS_MNT" || exit 1
+ fi
+
+ if [ ! -d "$INITRAMFS_MNT" ]; then
+ # we need at about 300 MiB on ubuntu, 200 on debian
+ # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
+ while IFS=" " read -r k v u; do
+ # /proc/meminfo format is documented in proc(5)
+ case "$u" in
+ MB) u=1048576;;
+ kB) u=1024;;
+ *) u=1;;
+ esac
+ case "$k" in
+ "MemAvailable:") MemAvailable=$((v*u));;
+ "SwapFree:") SwapFree=$((v*u));;
+ esac
+ done </proc/meminfo
+ if [ $((MemAvailable+SwapFree)) -lt $((300*1024*1024)) ]; then
+ log_error "Not enough memory available. Please close some programs or add swap space to suspend successfully."
+ exit 1
+ fi
+
+ mkdir -m0700 "$INITRAMFS_MNT"
+ mount -t ramfs -o nodev,mode=0700 ramfs "$INITRAMFS_MNT"
+
+ # extract initrd.img to initramfs dir
+ unmkinitramfs "$INITRAMFS" "$INITRAMFS_MNT"
+ new="y"
+ fi
+
+ # unmkinitramfs(8) extracts microcode into folders "early*" and the actual initramfs into "main"
+ if [ -f "$INITRAMFS_MNT/sbin/cryptsetup" ]; then
+ INITRAMFS_DIR="$INITRAMFS_MNT"
+ elif [ -f "$INITRAMFS_MNT/main/sbin/cryptsetup" ]; then
+ INITRAMFS_DIR="$INITRAMFS_MNT/main"
+ else
+ log_error "Directory $INITRAMFS_MNT has unpected content" >&2
+ exit 1
+ fi
+
+ if [ "$new" = "y" ]; then
+ for p in /dev /proc /run /sys; do
+ if [ ! -d "$INITRAMFS_DIR$p" ]; then
+ mkdir -m0755 "$INITRAMFS_DIR$p"
+ fi
+ done
+
+ # copy our binary to ramdisk
+ install -m0755 -t "$INITRAMFS_DIR/bin" /lib/cryptsetup/scripts/suspend/cryptsetup-suspend
+
+ # copy all firmware files to ramdisk to prevent dead-lock
+ # see https://salsa.debian.org/mejo/cryptsetup-suspend/issues/38)
+ # TODO we should try to identify which firmwares need to be loaded
+ # and only copy those
+ if [ -d /lib/firmware ] && [ ! -d "$INITRAMFS_DIR/lib/firmware" ]; then
+ cp -dR -T -- /lib/firmware "$INITRAMFS_DIR/lib/firmware"
+ fi
+ fi
+
+ # from initramfs-tools-core's /usr/share/initramfs-tools/init
+ mount -t devtmpfs -o noexec,nosuid,mode=0755 udev "$INITRAMFS_DIR/dev"
+ mount -t proc -o nodev,noexec,nosuid proc "$INITRAMFS_DIR/proc"
+ mount -t ramfs -o nodev,noexec,nosuid,mode=0755 ramfs "$INITRAMFS_DIR/run"
+ mount -t sysfs -o nodev,noexec,nosuid sysfs "$INITRAMFS_DIR/sys"
+
+ [ -d "$INITRAMFS_DIR/dev/pts" ] || mkdir -m0755 "$INITRAMFS_DIR/dev/pts"
+ mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts "$INITRAMFS_DIR/dev/pts" || true
+
+ # remount read-only, private and unbindable
+ mount -oremount,ro --make-rprivate --make-runbindable "$INITRAMFS_MNT"
+}
+
+umount_initramfs() {
+ if [ -d "${INITRAMFS_DIR-}" ]; then
+ umount -- "$INITRAMFS_DIR/dev/pts"
+ umount -- "$INITRAMFS_DIR/dev"
+ umount -- "$INITRAMFS_DIR/proc"
+ umount -- "$INITRAMFS_DIR/run"
+ umount -- "$INITRAMFS_DIR/sys"
+ fi
+ if [ "$KEEP_INITRAMFS" != "true" ] || [ -z "${INITRAMFS_DIR+x}" ]; then
+ # always unmount if we error out before setting INITRAMFS_DIR
+ umount -- "$INITRAMFS_MNT"
+ rmdir -- "$INITRAMFS_MNT"
+ fi
+}
+
+CGROUP_FREEZER=
+freeze_cgroup() {
+ local c="$1" v
+ # freeze cgroup if non-frozen
+ if [ -f "$c" ] && v="$(cat <"$c")" && [ $v -eq 0 ]; then
+ echo 1 >"$c"
+ CGROUP_FREEZER="$c${CGROUP_FREEZER:+" $CGROUP_FREEZER"}"
+ fi
+}
+freeze_cgroups() {
+ local mycgroup c
+
+ # freeze all machines/containers and user cgroups
+ freeze_cgroup "$hierarchy/machine.slice/cgroup.freeze"
+ freeze_cgroup "$hierarchy/user.slice/cgroup.freeze"
+
+ # get my second level cgroup
+ mycgroup="$(grep -m1 "^0::" /proc/self/cgroup | cut -sd/ -f3)"
+
+ # freeze all system cgroups except ours and systemd-suspend
+ for c in "$hierarchy"/system.slice/*/cgroup.freeze; do
+ if [ "$c" != "$hierarchy/system.slice/$mycgroup/cgroup.freeze" ] && \
+ [ "${c#"$hierarchy/system.slice/systemd-suspend."}" = "$c" ]; then
+ freeze_cgroup "$c"
+ fi
+ done
+
+ # freeze systemd itself
+ freeze_cgroup "$hierarchy/init.scope/cgroup.freeze"
+}
+
+thaw_cgroups() {
+ local c
+ for c in $CGROUP_FREEZER; do
+ echo 0 >"$c"
+ done
+}
+
+populate_ACTIVE_DEVICES() {
+ local DEV MAJ MIN
+ if ! dm_blkdevname "$CRYPTTAB_NAME" >/dev/null; then
+ # silently ignore unmapped devices
+ return 0
+ elif [ "$(dmsetup info --noheadings -c -o subsystem -- "$CRYPTTAB_NAME")" != "CRYPT" ]; then
+ cryptsetup_message "ERROR: $CRYPTTAB_NAME: Subsystem mismatch"
+ return 1
+ elif ! _resolve_device "$CRYPTTAB_SOURCE"; then
+ cryptsetup_message "ERROR: $CRYPTTAB_NAME: Missing source $CRYPTTAB_SOURCE"
+ return 1
+ elif [ "$(dmsetup info -c --noheadings -o devnos_used -- "$CRYPTTAB_NAME" 2>/dev/null)" != "$MAJ:$MIN" ]; then
+ cryptsetup_message "ERROR: $CRYPTTAB_NAME: Source mismatch"
+ return 1
+ fi
+
+ if ! crypttab_parse_options --quiet; then
+ cryptsetup_message "ERROR: $CRYPTTAB_NAME: Unable to parse options field"
+ return 1
+ elif [ "$CRYPTTAB_TYPE" != "luks" ]; then
+ # XXX does it even work with detached headers?
+ cryptsetup_message "WARNING: $CRYPTTAB_NAME: unable to suspend non-LUKS device"
+ return 0
+ fi
+
+ # XXX that's not robust since $CRYPTTAB_NAME might contain spaces or
+ # special characters; we need to create a NUL-delimited list in a
+ # file instead
+ ACTIVE_DEVICES="${ACTIVE_DEVICES:+"$ACTIVE_DEVICES "}$CRYPTTAB_NAME"
+}
+
+clean_up() {
+ # we always want to run through the whole cleanup
+ set +e
+
+ # thaw all frozen cgroups
+ thaw_cgroups
+
+ # Run post-suspend scripts
+ run_dir post suspend
+
+ umount_initramfs
+
+ # unlock sessions
+ if [ "$UNLOCK_SESSIONS" = "true" ]; then
+ loginctl unlock-sessions
+ fi
+}
+
+## Main script
+
+# check unified cgroups hierarchy
+# https://github.com/systemd/systemd/blob/master/docs/CGROUP_DELEGATION.md
+if [ -d /sys/fs/cgroup/system.slice ]; then
+ hierarchy="/sys/fs/cgroup"
+elif [ -d /sys/fs/cgroup/unified/system.slice ]; then
+ # hybrid cgroup hierarchy
+ hierarchy="/sys/fs/cgroup/unified"
+else
+ log_error "No unified cgroups hierarchy"
+ exit 1
+fi
+
+# check that not run as user
+# XXX: We should catch also cases where libpam-systemd is not installed
+if grep -Eq '^[0-9]+:[^:]*:/user\.slice/' /proc/self/cgroup; then
+ log_error "Don't run this script as user"
+ exit 1
+fi
+
+# always thaw cgroups, re-mount filesystems and remove initramfs at the end of the script
+trap clean_up EXIT
+
+read_config
+
+# extract temporary filesystem to switch to
+mount_initramfs
+
+# Run pre-suspend scripts
+run_dir pre suspend
+
+# populate list of active crypt devices
+ACTIVE_DEVICES=""
+crypttab_foreach_entry populate_ACTIVE_DEVICES
+
+# freeze all cgroups but us
+freeze_cgroups
+
+# No longer fail in case of errors
+set +e
+
+# change into ramdisk
+devices_remaining="$(chroot "$INITRAMFS_DIR" /bin/sh -c "
+ # suspend active luks devices (in reverse order) and system
+ /bin/cryptsetup-suspend --reverse $ACTIVE_DEVICES
+
+ TABFILE=\"/cryptroot/crypttab\"
+ . /lib/cryptsetup/functions
+
+ # resume active luks devices (only initramfs devices)
+ for dev in $ACTIVE_DEVICES; do
+ if crypttab_find_entry --quiet \"\$dev\"; then
+ DM_DISABLE_UDEV=y resume_device \"\$dev\" || sleep 5
+ else
+ # write remaining devices to FD3
+ printf \"%s \" \"\$dev\" >&3
+ fi
+ done
+" 3>&- 3>&1 >&2)"
+
+# resume remaining active luks devices (non-initramfs devices)
+for dev in $devices_remaining; do
+ if crypttab_find_entry --quiet "$dev"; then
+ # explicitely disable udev support, cf. #1020553
+ # XXX this is not ideal since udev might be required in some situations
+ # (detached header or key material on removable device comes to mind)
+ DM_DISABLE_UDEV=y resume_device "$dev" || true
+ else
+ log_error "'$dev' not found in /etc/crypttab"
+ fi
+done
diff --git a/debian/scripts/suspend/cryptsetup-suspend.c b/debian/scripts/suspend/cryptsetup-suspend.c
new file mode 100644
index 0000000..af1b6f6
--- /dev/null
+++ b/debian/scripts/suspend/cryptsetup-suspend.c
@@ -0,0 +1,225 @@
+/*
+ * Small program to LUKS suspend devices before system suspend
+ *
+ * Copyright: (c) 2018 Guilhem Moulin <guilhem@debian.org>
+ * (c) 2018-2020 Jonas Meurer <jonas@freesources.org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <libcryptsetup.h>
+
+#define SYSFS_POWER_SYNC_ON_SUSPEND "/sys/power/sync_on_suspend"
+#define SYSFS_POWER_STATE "/sys/power/state"
+
+void usage() {
+ printf("Usage: cryptsetup-suspend [-r|--reverse] <blkdev> [<blkdev> ...]\n"
+ " -r, --reverse process luks devices in reverse order\n\n");
+ exit(1);
+}
+
+/* Calculate free memory (MemAvailable + SwapFree) from /proc/meminfo */
+uint32_t get_mem_swap_avail_kb() {
+ FILE *meminfo = fopen("/proc/meminfo", "r");
+ if (meminfo == NULL)
+ err(EXIT_FAILURE, "couldn't open /proc/meminfo");
+
+ int mem_avail_kb, swap_free_kb = 0;
+ char line[256];
+ while (fgets(line, sizeof(line), meminfo)) {
+ if (strncmp(line, "MemAvailable", strlen("MemAvailable")) == 0) {
+ if (sscanf(line, "MemAvailable: %d kB", &mem_avail_kb) != 1)
+ errx(EXIT_FAILURE, "couldn't read MemAvailable from /proc/meminfo");
+ } else if (strncmp(line, "SwapFree", strlen("SwapFree")) == 0) {
+ if (sscanf(line, "SwapFree: %d kB", &swap_free_kb) != 1)
+ errx(EXIT_FAILURE, "couldn't read SwapFree from /proc/meminfo");
+ }
+ }
+ fclose(meminfo);
+
+ uint32_t mem_swap_avail_kb = mem_avail_kb + swap_free_kb;
+ if (mem_swap_avail_kb == 0)
+ errx(EXIT_FAILURE, "error reading available memory and swap from /proc/meminfo");
+
+ return mem_swap_avail_kb;
+}
+
+int main(int argc, char *argv[]) {
+ int rv = 0;
+ bool reverse = 0;
+ int d_size;
+ bool sync_on_suspend_reset = 0;
+ FILE *sos = NULL;
+
+ /* Process commandline arguments */
+ if (argc < 2) {
+ usage();
+ } else if ((strcmp(argv[1], "-r") == 0) || (strcmp(argv[1], "--reverse") == 0)) {
+ if (argc < 3)
+ usage();
+
+ reverse = 1;
+ d_size = argc-2;
+ } else {
+ d_size = argc-1;
+ }
+
+ /* Read in devices */
+ const char *devices[d_size];
+ if (!reverse) {
+ for (int i = 0; i < d_size; i++) {
+ devices[i] = argv[i+1];
+ }
+ } else {
+ for (int i = 0; i < d_size; i++) {
+ devices[i] = argv[argc-i-1];
+ }
+ }
+
+ /* Disable sync_on_suspend in Linux kernel
+ *
+ * Only available in Linux kernel >= 5.6 */
+ if (access(SYSFS_POWER_SYNC_ON_SUSPEND, W_OK) < 0) {
+ if (errno == ENOENT)
+ warnx("kernel too old, can't disable sync on suspend");
+ } else {
+ sos = fopen(SYSFS_POWER_SYNC_ON_SUSPEND, "r+");
+ if (!sos)
+ err(EXIT_FAILURE, "couldn't open sysfs file");
+
+ int sos_c = fgetc(sos);
+ if (fgetc(sos) == EOF)
+ err(EXIT_FAILURE, "couldn't read from file");
+
+ if (sos_c == '0') {
+ /* Already disabled */
+ } else if (sos_c == '1') {
+ sync_on_suspend_reset = 1;
+ if (fputc('0', sos) <= 0)
+ err(EXIT_FAILURE, "couldn't write to file");
+ } else {
+ errx(EXIT_FAILURE, "unexpected value from %s", SYSFS_POWER_SYNC_ON_SUSPEND);
+ }
+
+ fclose(sos);
+ }
+
+ /* Change process priority to -20 (highest) to avoid races between
+ * the LUKS suspend(s) and the suspend-on-ram. */
+ if (setpriority(PRIO_PROCESS, 0, -20) == -1)
+ warn("can't lower process priority to -20");
+
+ /* Get memory settings of keyslots from processed LUKS2 devices */
+ uint32_t argon2i_max_memory_kb = 0;
+ for (int i = 0; i < d_size; i++) {
+ struct crypt_device *cd = NULL;
+ if (crypt_init_by_name(&cd, devices[i])) {
+ warnx("couldn't init LUKS device %s", devices[i]);
+ rv = EXIT_FAILURE;
+ } else {
+ /* Only LUKS2 devices may use argon2i PBKDF */
+ if (strcmp(crypt_get_type(cd), CRYPT_LUKS2) != 0)
+ continue;
+ int ks_max = crypt_keyslot_max(crypt_get_type(cd));
+ for (int j = 0; j < ks_max; j++) {
+ crypt_keyslot_info ki = crypt_keyslot_status(cd, j);
+ /* Only look at active keyslots */
+ if (ki != CRYPT_SLOT_ACTIVE && ki != CRYPT_SLOT_ACTIVE_LAST)
+ continue;
+ struct crypt_pbkdf_type pbkdf_ki;
+ if (crypt_keyslot_get_pbkdf(cd, j, &pbkdf_ki) < 0) {
+ warn("couldn't get PBKDF for keyslot %d of device %s", j, devices[i]);
+ rv = EXIT_FAILURE;
+ } else {
+ if (pbkdf_ki.max_memory_kb > argon2i_max_memory_kb)
+ argon2i_max_memory_kb = pbkdf_ki.max_memory_kb;
+ }
+ }
+ }
+ crypt_free(cd);
+ }
+
+ /* Add some more memory to be on the safe side
+ * TODO: find a reasonable value */
+ argon2i_max_memory_kb += 2 * 1024; // 2MB
+
+ /* Check if we have enough memory available to prevent mlock() from
+ * triggering the OOM killer. */
+ uint32_t mem_swap_avail_kb = get_mem_swap_avail_kb();
+ if (argon2i_max_memory_kb > mem_swap_avail_kb) {
+ errx(EXIT_FAILURE, "Error: Available memory (%d kb) less than required (%d kb)",
+ mem_swap_avail_kb, argon2i_max_memory_kb);
+ }
+
+ /* Allocate and lock memory for later usage by LUKS resume in order to
+ * prevent swapping out after LUKS devices (which might include swap
+ * storage) have been suspended. */
+ fprintf(stderr, "Allocating and mlocking memory: %d kb\n", argon2i_max_memory_kb);
+ char *mem;
+ if (!(mem = malloc(argon2i_max_memory_kb)))
+ err(EXIT_FAILURE, "couldn't allocate enough memory");
+ if (mlock(mem, argon2i_max_memory_kb) == -1)
+ err(EXIT_FAILURE, "couldn't lock enough memory");
+ /* Fill the allocated memory to make sure it's really reserved even if
+ * memory pages are copy-on-write. */
+ size_t i;
+ size_t page_size = getpagesize();
+ for (i = 0; i < argon2i_max_memory_kb; i += page_size)
+ mem[i] = 0;
+
+ /* Do the final filesystem sync since we disabled sync_on_suspend in
+ * Linux kernel. */
+ sync();
+
+ for (int i = 0; i < d_size; i++) {
+ struct crypt_device *cd = NULL;
+ if (crypt_init_by_name(&cd, devices[i]) || crypt_suspend(cd, devices[i])) {
+ warnx("couldn't suspend LUKS device %s", devices[i]);
+ rv = EXIT_FAILURE;
+ }
+ crypt_free(cd);
+ }
+
+ fprintf(stderr, "Sleeping...\n");
+ FILE *s = fopen(SYSFS_POWER_STATE, "w");
+ if (!s)
+ err(EXIT_FAILURE, "failed to open %s", SYSFS_POWER_STATE);
+ if (fputs("mem", s) <= 0)
+ err(EXIT_FAILURE, "couldn't write to %s", SYSFS_POWER_STATE);
+ fclose(s);
+ fprintf(stderr, "Resuming...\n");
+
+ /* Restore original sync_on_suspend value */
+ if (sync_on_suspend_reset) {
+ sos = fopen(SYSFS_POWER_SYNC_ON_SUSPEND, "w");
+ if (!sos)
+ err(EXIT_FAILURE, "couldn't open sysfs file");
+ if (fputc('1', sos) <= 0)
+ err(EXIT_FAILURE, "couldn't write to file");
+ fclose(sos);
+ }
+
+ return rv;
+}
diff --git a/debian/scripts/suspend/cryptsetup-suspend.shutdown b/debian/scripts/suspend/cryptsetup-suspend.shutdown
new file mode 100644
index 0000000..f7d9f5d
--- /dev/null
+++ b/debian/scripts/suspend/cryptsetup-suspend.shutdown
@@ -0,0 +1,3 @@
+#!/bin/sh
+umount -R /run/cryptsetup/cryptsetup-suspend-initramfs
+rmdir /run/cryptsetup/cryptsetup-suspend-initramfs
diff --git a/debian/scripts/suspend/suspend.conf b/debian/scripts/suspend/suspend.conf
new file mode 100644
index 0000000..79b2287
--- /dev/null
+++ b/debian/scripts/suspend/suspend.conf
@@ -0,0 +1,10 @@
+# Caution: This file will be sourced by another script.
+# For security reasons, it should only be writable by root.
+
+# Automatically unlock user sessions after resume
+# UNLOCK_SESSIONS="false"
+
+# Keep unpacked initramfs in RAM to accelerate suspension (this setting
+# is ignored when the default initramfs image is newer than the
+# cached/unpacked image)
+# KEEP_INITRAMFS="false"
diff --git a/debian/scripts/suspend/systemd/cryptsetup-suspend.conf b/debian/scripts/suspend/systemd/cryptsetup-suspend.conf
new file mode 100644
index 0000000..10664cf
--- /dev/null
+++ b/debian/scripts/suspend/systemd/cryptsetup-suspend.conf
@@ -0,0 +1,12 @@
+[Service]
+# Protect against OOM killer. luksResume with Argon2 needs a lot of memory
+OOMScoreAdjust=-1000
+# Give us higher priority
+Nice=-10
+# override ExecStart of systemd-suspend.service
+ExecStart=
+# use VT 8 as workaround for https://gitlab.gnome.org/GNOME/gdm/issues/527
+# XXX on systems specifying the console= kernel parameter (such as a serial
+# port) we should probably honor it
+ExecStart=/bin/openvt -ws -c8 \
+ /lib/cryptsetup/scripts/suspend/cryptsetup-suspend-wrapper