123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- #!/bin/bash -e
- #
- # Copyright (c) 2017 Red Hat, Inc.
- # Copyright (c) 2017 Shawn Rose
- # Copyright (c) 2017 Guilhem Moulin
- #
- # Author: Harald Hoyer <harald@redhat.com>
- # Author: Nathaniel McCallum <npmccallum@redhat.com>
- # Author: Shawn Rose <shawnandrewrose@gmail.com>
- # Author: Guilhem Moulin <guilhem@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/>.
- #
- case $1 in
- prereqs) exit 0;;
- esac
- # Return 0 if $pid has a file descriptor pointing to $name, 1 otherwise
- in_fds() {
- local pid="$1" name="$2" fd
- for fd in /proc/$pid/fd/*; do
- if [ -e "$fd" ]; then
- [ "$(readlink -f "$fd")" != "$name" ] || return 0
- fi
- done
- return 1
- }
- # Print the PID of the askpass process with a file descriptor opened to
- # /lib/cryptsetup/passfifo if there is one.
- get_askpass_pid() {
- psinfo=$(ps) # Doing this so I don't end up matching myself
- echo "$psinfo" | awk "/$cryptkeyscript/ { print \$1 }" | while read -r pid; do
- if in_fds "$pid" "$PASSFIFO"; then
- echo "$pid"
- break
- fi
- done
- }
- luks1_decrypt() {
- luksmeta load "$@" \
- | clevis decrypt
- local rc
- for rc in "${PIPESTATUS[@]}"; do
- [ $rc -eq 0 ] || return $rc
- done
- return 0
- }
- luks2_jwe() {
- # jose jwe fmt -c outputs extra \n, so clean it up
- cryptsetup token export "$@" \
- | jose fmt -j- -Og jwe -o- \
- | jose jwe fmt -i- -c \
- | tr -d '\n'
- local rc
- for rc in "${PIPESTATUS[@]}"; do
- [ $rc -eq 0 ] || return $rc
- done
- return 0
- }
- # Wait for askpass, and then try and decrypt immediately. Just in case
- # there are multiple devices that need decrypting, this will loop
- # infinitely (The local-bottom script will kill this after decryption)
- clevisloop()
- {
- set -e
- # Set the path how we want it (Probably not all needed)
- PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin"
- if [ -x /bin/plymouth ] && plymouth --ping; then
- cryptkeyscript='plymouth ask-for-password'
- else
- # This has to be escaped for awk
- cryptkeyscript='\/lib\/cryptsetup\/askpass'
- fi
- PASSFIFO='/usr/lib/cryptsetup/passfifo'
- OLD_CRYPTTAB_SOURCE=""
- while true; do
- pid=$(get_askpass_pid)
- until [ "$pid" ] && [ -p "$PASSFIFO" ]; do
- sleep .1
- pid=$(get_askpass_pid)
- done
- # Import CRYPTTAB_SOURCE from the askpass process.
- local "$(grep '^CRYPTTAB_SOURCE=' /proc/"$pid"/environ)"
- # Make sure that CRYPTTAB_SOURCE is actually a block device
- [ ! -b "$CRYPTTAB_SOURCE" ] && continue
- # Make the source has changed if needed
- [ "$CRYPTTAB_SOURCE" = "$OLD_CRYPTTAB_SOURCE" ] && continue
- OLD_CRYPTTAB_SOURCE="$CRYPTTAB_SOURCE"
- UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e
- if cryptsetup isLuks --type luks1 "$CRYPTTAB_SOURCE"; then
- # If the device is not initialized, sliently skip it.
- luksmeta test -d "$CRYPTTAB_SOURCE" || continue
- luksmeta show -d "$CRYPTTAB_SOURCE" | while read -r slot state uuid; do
- [ "$state" == "active" ] || continue
- [ "$uuid" == "$UUID" ] || continue
- if pt="$(luks1_decrypt -d "$CRYPTTAB_SOURCE" -s "$slot" -u "$UUID")"; then
- echo -n "$pt" > "$PASSFIFO"
- break
- fi
- done
- elif cryptsetup isLuks --type luks2 "$CRYPTTAB_SOURCE"; then
- cryptsetup luksDump "$CRYPTTAB_SOURCE" | sed -rn 's|^\s+([0-9]+): clevis|\1|p' | while read -r id; do
- jwe="$(luks2_jwe --token-id "$id" "$CRYPTTAB_SOURCE")" \
- || continue
- if pt="$(echo -n "$jwe" | clevis decrypt)"; then
- echo -n "$pt" > "$PASSFIFO"
- break
- fi
- done
- fi
- # Now that the current device has its password, let's sleep a
- # bit. This gives cryptsetup time to actually decrypt the
- # device and prompt for the next password if needed.
- sleep .5
- done
- }
- . /scripts/functions
- # This is a copy of 'all_netbootable_devices/all_non_enslaved_devices' for
- # platforms that might not provide it.
- clevis_all_netbootable_devices() {
- for device in /sys/class/net/* ; do
- if [ ! -e "$device/flags" ]; then
- continue
- fi
- loop=$(($(cat "$device/flags") & 0x8 && 1 || 0))
- bc=$(($(cat "$device/flags") & 0x2 && 1 || 0))
- ptp=$(($(cat "$device/flags") & 0x10 && 1 || 0))
- # Skip any device that is a loopback
- if [ $loop = 1 ]; then
- continue
- fi
- # Skip any device that isn't a broadcast
- # or point-to-point.
- if [ $bc = 0 ] && [ $ptp = 0 ]; then
- continue
- fi
- # Skip any enslaved device (has "master" link
- # attribute on it)
- device=$(basename "$device")
- ip -o link show "$device" | grep -q -w master && continue
- DEVICE="$DEVICE $device"
- done
- echo "$DEVICE"
- }
- # Check if network is up before trying to configure it.
- eth_check() {
- for device in $(clevis_all_netbootable_devices); do
- ip link set dev "$device" up
- sleep 1
- ETH_HAS_CARRIER=$(cat /sys/class/net/"$device"/carrier)
- if [ "$ETH_HAS_CARRIER" = '1' ]; then
- return 0
- fi
- done
- return 1
- }
- if eth_check; then
- # Make sure networking is set up: if booting via nfs, it already is
- # Doesn't seem to work when added to clevisloop for some reason
- [ "$boot" = nfs ] || configure_networking
- fi
- clevisloop &
- echo $! > /run/clevis.pid
|