123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- #!/bin/bash
- #
- # 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 fifo path or nothing if not found
- get_fifo_path() {
- local pid="$1"
- for fd in /proc/$pid/fd/*; do
- if [ -e "$fd" ]; then
- if [[ $(readlink -f "${fd}") == *"/cryptsetup/passfifo" ]]; then
- readlink -f "${fd}"
- fi
- fi
- done
- }
- # Print the PID of the askpass process and fifo path with a file descriptor opened to
- 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
- pf=$(get_fifo_path "${pid}")
- if [[ $pf != "" ]]; then
- echo "${pid} ${pf}"
- break
- fi
- done
- }
- luks1_decrypt() {
- local CRYPTTAB_SOURCE=$1
- local PASSFIFO=$2
- UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e
- luksmeta show -d "$CRYPTTAB_SOURCE" | while read -r slot state uuid; do
- [ "$state" == "active" ] || continue
- [ "$uuid" == "$UUID" ] || continue
- lml=$(luksmeta load -d "${CRYPTTAB_SOURCE}" -s "${slot}" -u "${UUID}")
- [ $? -eq 0 ] || continue
- decrypted=$(echo -n "${lml}" | clevis decrypt 2>/dev/null)
- [ $? -eq 0 ] || continue
- # Fail safe
- [ "$decrypted" != "" ] || continue
- echo -n "${decrypted}" >"$PASSFIFO"
- return 0
- done
- return 1
- }
- luks2_decrypt() {
- local CRYPTTAB_SOURCE=$1
- local PASSFIFO=$2
- cryptsetup luksDump "$CRYPTTAB_SOURCE" | sed -rn 's|^\s+([0-9]+): clevis|\1|p' | while read -r id; do
- # jose jwe fmt -c outputs extra \n, so clean it up
- cte=$(cryptsetup token export --token-id "$id" "$CRYPTTAB_SOURCE")
- [ $? -eq 0 ] || continue
- josefmt=$(echo "${cte}" | jose fmt -j- -Og jwe -o-)
- [ $? -eq 0 ] || continue
- josejwe=$(echo "${josefmt}" | jose jwe fmt -i- -c)
- [ $? -eq 0 ] || continue
- jwe=$(echo "${josejwe}" | tr -d '\n')
- [ $? -eq 0 ] || continue
- decrypted=$(echo -n "${jwe}" | clevis decrypt 2>/dev/null)
- [ $? -eq 0 ] || continue
- # Fail safe
- [ "$decrypted" != "" ] || continue
- echo -n "${decrypted}" >"$PASSFIFO"
- return 0
- done
- return 1
- }
- has_tang_pin() {
- local dev="$1"
- clevis luks list -d "${dev}" | grep -q tang
- }
- # 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 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
- OLD_CRYPTTAB_SOURCE=""
- local netcfg_attempted=0
- while true; do
- # Re-get the askpass PID in case there are multiple encrypted devices
- pid=""
- until [ "$pid" ] && [ -p "$PASSFIFO" ]; do
- sleep .1
- pid_fifo=$(get_askpass_pid)
- pid=$(echo "${pid_fifo}" | cut -d' ' -f1)
- PASSFIFO=$(echo "${pid_fifo}" | cut -d' ' -f2-)
- done
- # Import CRYPTTAB_SOURCE from the askpass process.
- local CRYPTTAB_SOURCE="$(cat /proc/${pid}/environ 2> /dev/null | \
- tr '\0' '\n' | grep '^CRYPTTAB_SOURCE=' | cut -d= -f2)"
- [ -n "$CRYPTTAB_SOURCE" ] || continue
- # Make sure that CRYPTTAB_SOURCE is actually a block device
- [ ! -b "$CRYPTTAB_SOURCE" ] && continue
- sleep .1
- # Make the source has changed if needed
- [ "$CRYPTTAB_SOURCE" = "$OLD_CRYPTTAB_SOURCE" ] && continue
- OLD_CRYPTTAB_SOURCE="$CRYPTTAB_SOURCE"
- if [ $netcfg_attempted -eq 0 ] && has_tang_pin ${CRYPTTAB_SOURCE}; then
- netcfg_attempted=1
- do_configure_networking
- fi
- if cryptsetup isLuks --type luks1 "$CRYPTTAB_SOURCE"; then
- # If the device is not initialized, sliently skip it.
- luksmeta test -d "$CRYPTTAB_SOURCE" || continue
- if luks1_decrypt "${CRYPTTAB_SOURCE}" "${PASSFIFO}"; then
- echo "Unlocked ${CRYPTTAB_SOURCE} with clevis"
- else
- OLD_CRYPTTAB_SOURCE=""
- sleep 5
- fi
- elif cryptsetup isLuks --type luks2 "$CRYPTTAB_SOURCE"; then
- if luks2_decrypt "${CRYPTTAB_SOURCE}" "${PASSFIFO}"; then
- echo "Unlocked ${CRYPTTAB_SOURCE} with clevis"
- else
- OLD_CRYPTTAB_SOURCE=""
- sleep 5
- fi
- 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
- if [ -z "$DEVICE" ]; then
- DEVICE="$device"
- else
- DEVICE="$DEVICE $device"
- fi
- done
- echo "$DEVICE"
- }
- get_specified_device() {
- local dev="$(echo $IP | cut -d: -f6)"
- [ -z "$dev" ] || echo $dev
- }
- # Workaround configure_networking() not waiting long enough for an interface
- # to appear. This code can be removed once that has been fixed in all the
- # distro releases we care about.
- wait_for_device() {
- local device="$(get_specified_device)"
- local ret=0
- if [ -n "$device" ]; then
- log_begin_msg "clevis: Waiting for interface ${device} to become available"
- local netdev_wait=0
- while [ $netdev_wait -lt 10 ]; do
- if [ -e "/sys/class/net/${device}" ]; then
- break
- fi
- netdev_wait=$((netdev_wait + 1))
- sleep 1
- done
- if [ ! -e "/sys/class/net/${device}" ]; then
- log_failure_msg "clevis: Interface ${device} did not appear in time"
- ret=1
- fi
- log_end_msg
- fi
- wait_for_udev 10
- return $ret
- }
- do_configure_networking() {
- # Make sure networking is set up: if booting via nfs, it already is
- if [ "$boot" != nfs ] && wait_for_device; then
- clevis_net_cnt=$(clevis_all_netbootable_devices | tr ' ' '\n' | wc -l)
- if [ -z "$IP" ] && [ "$clevis_net_cnt" -gt 1 ]; then
- echo ""
- echo "clevis: Warning: multiple network interfaces available but no ip= parameter provided."
- fi
- configure_networking
- fi
- }
- clevisloop &
- echo $! >/run/clevis.pid
|