On Fri, 18 Dec 2015 at 19:16:56 -0500, Richard Hansen wrote: > * why SIGKILL instead of SIGTERM? seems too aggressive > * perhaps add a waitpid() after the kill() to ensure that a second > plymouth won't be run before the first one exits
Agreed, but unfortunately plymouth doesn't terminate on SIGTERM. > * why does cryptroot-unlock use /bin/ash instead of /bin/sh? > * there are lots of BusyBox ashisms in the cryptroot-unlock script, > many of which can be easily replaced with POSIX conformant code POSIX's read builtin doesn't support the -s flag. Sure we can replace with stty with a trap to restore echo, but since busybox is a dependency anyway I don't think it's worth it :-P I've addressed the rest in the updated patch. Thanks for your input! -- Guilhem.
diff --git a/debian/askpass.c b/debian/askpass.c
index d234879..6750385 100644
--- a/debian/askpass.c
+++ b/debian/askpass.c
@@ -38,11 +38,7 @@
#include <sys/select.h>
#include <sys/ioctl.h>
#include <signal.h>
-#include <dirent.h>
-#include <linux/vt.h>
-#include <sys/socket.h>
#include <sys/un.h>
-#include <sys/uio.h>
#define DEBUG 0
@@ -216,65 +212,76 @@ systemd_finish(int fd)
}
/*****************************************************************************
- * splashy functions *
+ * plymouth functions *
*****************************************************************************/
-/* It might be better style to just do a popen of splashy_update ? */
-
-#define SPLASHY_SOCK "\0/splashy"
-static size_t splashyused = 0;
-static size_t splashysize = 0;
-static char *splashybuf = NULL;
+#define PLYMOUTH_PATH "/bin/plymouth"
+static pid_t plymouthpid;
+static size_t plymouthused = 0;
+static size_t plymouthsize = 0;
+static char *plymouthbuf = NULL;
static int
-splashy_prepare(const char *prompt)
+plymouth_prepare(const char *prompt)
{
- int fd;
- struct sockaddr addr = {AF_UNIX, SPLASHY_SOCK};
- struct iovec iov[2];
+ int pipefds[2];
- if ((fd = socket (PF_UNIX, SOCK_STREAM, 0)) == -1) {
+ if (access(PLYMOUTH_PATH, X_OK))
return -1;
- }
- if (connect (fd, &addr, sizeof addr) == -1) {
- close (fd);
+ if (system(PLYMOUTH_PATH" --ping"))
return -1;
- }
- iov[0].iov_base = "getpass ";
- iov[0].iov_len = strlen ("getpass ");
- iov[1].iov_base = (char *)prompt;
- iov[1].iov_len = strlen (prompt) + 1;
+ /* Plymouth will add a ':' if it is a non-graphical prompt */
+ char *prompt2 = strdup(prompt);
+ int len = strlen(prompt2);
+ if (len > 1 && prompt2[len-2] == ':' && prompt2[len-1] == ' ')
+ prompt2[len-2] = '\0';
+ else if (len > 0 && prompt2[len-1] == ':')
+ prompt2[len-1] = '\0';
- if (writev (fd, iov, 2) == -1) {
- close (fd);
+ if (pipe(pipefds))
+ return -1;
+
+ plymouthpid = fork();
+ if (plymouthpid < 0) {
+ close(pipefds[0]);
+ close(pipefds[1]);
return -1;
}
- /* Shutdown write? */
+ if (plymouthpid == 0) {
+ close(pipefds[0]);
+ if (dup2(pipefds[1], STDOUT_FILENO) < 0)
+ exit(EXIT_FAILURE);
+ execl(PLYMOUTH_PATH, PLYMOUTH_PATH,
+ "ask-for-password", "--prompt", prompt2, (char*)NULL);
+ exit(EXIT_FAILURE);
+ }
+ free(prompt2);
- return fd;
+ close(pipefds[1]);
+ return pipefds[0];
}
static bool
-splashy_read(int fd, char **buf, size_t *size)
+plymouth_read(int fd, char **buf, size_t *size)
{
- debug("In splashy_read\n");
- if (fifo_common_read(fd, &splashybuf, &splashyused, &splashysize)) {
- *buf = splashybuf;
- *size = splashyused;
+ debug("In plymouth_read\n");
+ if (fifo_common_read(fd, &plymouthbuf, &plymouthused, &plymouthsize)) {
+ *buf = plymouthbuf;
+ *size = plymouthused;
return true;
}
return false;
}
-
static void
-splashy_finish(int fd)
+plymouth_finish(int fd)
{
- fifo_common_finish (fd, &splashybuf, &splashyused, &splashysize);
+ kill(plymouthpid, SIGKILL);
+ fifo_common_finish(fd, &plymouthbuf, &plymouthused, &plymouthsize);
}
/*****************************************************************************
@@ -448,8 +455,8 @@ struct method {
static struct method methods[] = {
{ "systemd", systemd_prepare, systemd_read, systemd_finish, true, false, true, -1 },
- { "splashy", splashy_prepare, splashy_read, splashy_finish, false, false, true, -1 },
{ "fifo", fifo_prepare, fifo_read, fifo_finish, false, false, true, -1 },
+ { "plymouth", plymouth_prepare, plymouth_read, plymouth_finish, true, false, true, -1 },
{ "console", console_prepare, console_read, console_finish, false, false, true, -1 }
};
diff --git a/debian/cryptsetup.dirs b/debian/cryptsetup.dirs
index 94c9a56..f4663f9 100644
--- a/debian/cryptsetup.dirs
+++ b/debian/cryptsetup.dirs
@@ -10,5 +10,6 @@
/usr/share/initramfs-tools/scripts/local-bottom
/usr/share/initramfs-tools/scripts/local-block
/usr/share/initramfs-tools/conf-hooks.d
+/usr/share/initramfs-tools/bin
/usr/share/man/man5
/usr/share/man/man8
diff --git a/debian/initramfs/cryptroot-script b/debian/initramfs/cryptroot-script
index 3e8281a..1c313c1 100644
--- a/debian/initramfs/cryptroot-script
+++ b/debian/initramfs/cryptroot-script
@@ -292,14 +292,8 @@ setup_mapping()
diskname="$cryptsource ($crypttarget)"
fi
- if [ -x /bin/plymouth ] && plymouth --ping; then
- cryptkeyscript="plymouth ask-for-password --prompt"
- # Plymouth will add a : if it is a non-graphical prompt
- cryptkey="Please unlock disk $diskname"
- else
- cryptkeyscript="/lib/cryptsetup/askpass"
- cryptkey="Please unlock disk $diskname: "
- fi
+ cryptkeyscript=/lib/cryptsetup/askpass
+ cryptkey="Please unlock disk $diskname: "
fi
diff --git a/debian/initramfs/cryptroot-unlock b/debian/initramfs/cryptroot-unlock
new file mode 100755
index 0000000..0bdfc55
--- /dev/null
+++ b/debian/initramfs/cryptroot-unlock
@@ -0,0 +1,125 @@
+#!/bin/ash
+
+# Remotely unlock encrypted volumes.
+#
+# Copyright © 2015 Guilhem Moulin <[email protected]>
+#
+# 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/>.
+
+set -ue
+PATH=/sbin:/bin
+
+TIMEOUT=10
+PASSFIFO=/lib/cryptsetup/passfifo
+ASKPASS=/lib/cryptsetup/askpass
+
+# Return 0 if $pid has a file descriptor pointing to $name, and 1
+# otherwise.
+in_fds() {
+ local pid="$1" name="$2" fd
+ for fd in $(find "/proc/$pid/fd" -type l); do
+ [ "$(readlink -f "$fd")" != "$name" ] || return 0
+ done
+ return 1
+}
+
+# Print the PID of the askpass process with a file descriptor opened to
+# /lib/cryptsetup/passfifo.
+get_askpass_pid() {
+ ps -eo pid,args | sed -nr "s#^\s*([0-9]+)\s+$ASKPASS\s+.*#\1#p" | while read pid; do
+ if in_fds "$pid" "$PASSFIFO"; then
+ echo "$pid"
+ break
+ fi
+ done
+}
+
+# Wait for askpass, then set $PID (resp. $BIRTH) to the PID (resp.
+# birth date) of the cryptsetup process with same $CRYPTTAB_NAME.
+wait_for_prompt() {
+ local pid=$(get_askpass_pid) timer=$(( 10 * $TIMEOUT ))
+
+ # wait for the fifo
+ until [ "$pid" ] && [ -p "$PASSFIFO" ]; do
+ sleep .1
+ pid=$(get_askpass_pid)
+ timer=$(( $timer - 1 ))
+ if [ $timer -le 0 ]; then
+ echo "Error: Timeout reached while waiting for askpass." >&2
+ exit 1
+ fi
+ done
+
+ # find the cryptsetup process with same $CRYPTTAB_NAME
+ eval $(grep -Ez '^CRYPTTAB_(NAME|TRIED|SOURCE)=' "/proc/$pid/environ" | tr '\0' '\n')
+ for pid in $(ps -eo pid,args | sed -nr 's#^\s*([0-9]+)\s+/sbin/cryptsetup\s+.*#\1#p'); do
+ if grep -Fxqz "CRYPTTAB_NAME=$CRYPTTAB_NAME" "/proc/$pid/environ"; then
+ PID=$pid
+ BIRTH=$(stat -c'%Z' "/proc/$PID")
+ return 0;
+ fi
+ done
+
+ PID=
+ BIRTH=
+}
+
+# Wait until $PID no longer exists or has a birth date greater that
+# $BIRTH (ie was reallocated). Then return with exit value 0 if
+# /dev/mapper/$CRYPTTAB_NAME exists, and with exit value 1 if the
+# maximum number of tries exceeded. Otherwise (if the unlocking
+# failed), return with value 1.
+wait_for_answer() {
+ local timer=$(( 10 * $TIMEOUT ))
+ until [ ! -d "/proc/$PID" ] || [ $(stat -c'%Z' "/proc/$PID") -gt $BIRTH ]; do
+ sleep .1
+ timer=$(( $timer - 1 ))
+ if [ $timer -le 0 ]; then
+ echo "Error: Timeout reached while waiting for PID $PID." >&2
+ exit 1
+ fi
+ done
+
+ if [ -e "/dev/mapper/$CRYPTTAB_NAME" ]; then
+ echo "cryptsetup: $CRYPTTAB_NAME set up successfully" >&2
+ exit 0
+ elif [ $CRYPTTAB_TRIED -ge 2 ]; then
+ echo "cryptsetup: maximum number of tries exceeded for $CRYPTTAB_NAME" >&2
+ exit 1
+ else
+ echo "cryptsetup: cryptsetup failed, bad password or options?" >&2
+ return 1
+ fi
+}
+
+if [ -t 0 ] && [ -x "$ASKPASS" ]; then
+ # interactive mode on a TTY: keep trying until successful or
+ # maximum number of tries exceeded.
+ while :; do
+ wait_for_prompt
+ diskname="$CRYPTTAB_NAME"
+ [ "${CRYPTTAB_SOURCE#/dev/disk/by-uuid/}" != "$CRYPTTAB_SOURCE" ] || diskname="$diskname ($CRYPTTAB_SOURCE)"
+ read -rs -p "Please unlock disk $diskname: "; echo
+ printf '%s' "$REPLY" >"$PASSFIFO"
+ wait_for_answer || true
+ done
+else
+ # non-interactive mode: slurp the passphrase from stdin
+ wait_for_prompt
+ diskname="$CRYPTTAB_NAME"
+ [ "${CRYPTTAB_SOURCE#/dev/disk/by-uuid/}" != "$CRYPTTAB_SOURCE" ] || diskname="$diskname ($CRYPTTAB_SOURCE)"
+ echo "Please unlock disk $diskname"
+ cat >"$PASSFIFO"
+ wait_for_answer || exit 1
+fi
diff --git a/debian/initramfs/cryptroot-unlock-hook b/debian/initramfs/cryptroot-unlock-hook
new file mode 100755
index 0000000..79a21af
--- /dev/null
+++ b/debian/initramfs/cryptroot-unlock-hook
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+PREREQ=""
+
+prereqs()
+{
+ echo "$PREREQ"
+}
+
+case "$1" in
+ prereqs)
+ prereqs
+ exit 0
+ ;;
+esac
+
+cp -p /usr/share/initramfs-tools/bin/cryptroot-unlock "$DESTDIR/bin/unlock"
+
+if [ -f /etc/initramfs-tools/etc/motd ]; then
+ cp /etc/initramfs-tools/etc/motd "$DESTDIR/etc/motd"
+else
+ cat >>"$DESTDIR/etc/motd" <<- EOF
+ To unlock root partition, and maybe others like swap, run \`unlock\`
+ EOF
+fi
diff --git a/debian/rules b/debian/rules
index 489ff9e..bf571fe 100755
--- a/debian/rules
+++ b/debian/rules
@@ -138,6 +138,10 @@ install-stamp: build-stamp
$(CURDIR)/debian/cryptsetup/usr/share/initramfs-tools/conf-hooks.d/cryptsetup
install -m 0755 debian/initramfs/cryptroot-hook \
$(CURDIR)/debian/cryptsetup/usr/share/initramfs-tools/hooks/cryptroot
+ install -m 0755 debian/initramfs/cryptroot-unlock-hook \
+ $(CURDIR)/debian/cryptsetup/usr/share/initramfs-tools/hooks/cryptroot-unlock
+ install -m 0755 debian/initramfs/cryptroot-unlock \
+ $(CURDIR)/debian/cryptsetup/usr/share/initramfs-tools/bin/cryptroot-unlock
install -m 0755 debian/initramfs/cryptroot-script \
$(CURDIR)/debian/cryptsetup/usr/share/initramfs-tools/scripts/local-top/cryptroot
install -m 0755 debian/initramfs/cryptroot-script-block \
signature.asc
Description: PGP signature

