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 <guil...@guilhem.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/>. + +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