123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- #!/bin/bash -e
- # vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
- #
- # Copyright (c) 2016 Red Hat, Inc.
- # Author: Harald Hoyer <harald@redhat.com>
- # Author: Nathaniel McCallum <npmccallum@redhat.com>
- #
- # 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/>.
- #
- SUMMARY="Binds a LUKS device using the specified policy"
- UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e
- # We require cryptsetup >= 2.0.4 to fully support LUKSv2.
- # Support is determined at build time.
- function luks2_supported() {
- return @OLD_CRYPTSETUP@
- }
- function usage() {
- exec >&2
- echo
- echo "Usage: clevis luks bind [-f] [-s SLT] [-k KEY] -d DEV PIN CFG"
- echo
- echo "$SUMMARY":
- echo
- echo " -f Do not prompt for LUKSMeta initialization"
- echo
- echo " -d DEV The LUKS device on which to perform binding"
- echo
- echo " -s SLT The LUKS slot to use"
- echo
- echo " -k KEY Non-interactively read LUKS password from KEY file"
- echo " -k - Non-interactively read LUKS password from standard input"
- echo
- exit 2
- }
- if [ $# -eq 1 ] && [ "$1" == "--summary" ]; then
- echo "$SUMMARY"
- exit 0
- fi
- FRC=()
- while getopts ":hfd:s:k:" o; do
- case "$o" in
- f) FRC+=(-f);;
- d) DEV="$OPTARG";;
- s) SLT="$OPTARG";;
- k) KEY="$OPTARG";;
- *) usage;;
- esac
- done
- if [ -z "$DEV" ]; then
- echo "Did not specify a device!" >&2
- usage
- fi
- if ! cryptsetup isLuks "$DEV"; then
- echo "$DEV is not a LUKS device!" >&2
- exit 1
- fi
- if ! PIN="${@:$((OPTIND++)):1}" || [ -z "$PIN" ]; then
- echo "Did not specify a pin!" >&2
- usage
- fi
- if ! CFG="${@:$((OPTIND++)):1}" || [ -z "$CFG" ]; then
- echo "Did not specify a pin config!" >&2
- usage
- fi
- if luks2_supported; then
- if cryptsetup isLuks --type luks1 "$DEV"; then
- luks_type="luks1"
- elif cryptsetup isLuks --type luks2 "$DEV";then
- luks_type="luks2"
- else
- echo "$DEV is not a supported LUKS device!" >&2
- exit 1
- fi
- else
- luks_type="luks1"
- fi
- if [ "${luks_type}" = "luks1" ]; then
- # The first free slot, as per cryptsetup. In connection to bug #70, we may
- # have to wipe out the LUKSMeta slot priot to adding the new key.
- first_free_cs_slot=$(cryptsetup luksDump "${DEV}" \
- | sed -rn 's|^Key Slot ([0-7]): DISABLED$|\1|p' \
- | head -n 1)
- if [ -z "${first_free_cs_slot}" ]; then
- echo "There are no more free slots in ${DEV}!" >&2
- exit 1
- fi
- fi
- if [ -n "$KEY" ]; then
- if [ "$KEY" == "-" ]; then
- if [ "$luks_type" == "luks1" ]; then
- if ! luksmeta test -d "$DEV" && [ -z "${FRC[*]}" ]; then
- echo "Cannot use '-k-' without '-f' unless already initialized!" >&2
- usage
- fi
- fi
- elif ! [ -f "$KEY" ]; then
- echo "Key file '$KEY' not found!" >&2
- exit 1
- fi
- fi
- # Generate a key with the same entropy as the LUKS Master Key
- key="$(pwmake "$(
- cryptsetup luksDump "$DEV" \
- | if [ "$luks_type" == "luks1" ]; then
- sed -rn 's|MK bits:[ \t]*([0-9]+)|\1|p'
- else
- sed -rn 's|^\s+Key:\s+([0-9]+) bits\s*$|\1|p'
- fi | sort -n | tail -n 1
- )")"
- # Encrypt the new key
- jwe="$(echo -n "$key" | clevis encrypt "$PIN" "$CFG")"
- # If necessary, initialize the LUKS volume
- if [ "$luks_type" == "luks1" ] && ! luksmeta test -d "$DEV"; then
- luksmeta init -d "$DEV" "${FRC[@]}"
- fi
- # Get the existing key.
- case "$KEY" in
- "") read -r -s -p "Enter existing LUKS password: " existing_key; echo;;
- -) existing_key="$(/bin/cat)";;
- *) ! IFS= read -rd '' existing_key < "$KEY";;
- esac
- # Check if the key is valid.
- if ! cryptsetup luksOpen --test-passphrase "${DEV}" \
- --key-file <(echo -n "${existing_key}"); then
- exit 1
- fi
- if [ "$luks_type" == "luks1" ]; then
- # In certain circumstances, we may have LUKSMeta slots "not in sync" with
- # cryptsetup, which means we will try to save LUKSMeta metadata over an
- # already used or partially used slot -- github issue #70.
- # If that is the case, let's wipe the LUKSMeta slot here prior to saving.
- if read -r _ state uuid < <(luksmeta show -d "${DEV}" \
- | grep "^${first_free_cs_slot} *"); then
- if [ "${state}" = "inactive" ] && [ "${uuid}" = "${UUID}" ]; then
- luksmeta wipe -f -d "${DEV}" -s "${first_free_cs_slot}"
- fi
- fi
- fi
- # Add the new key.
- if [ -n "$SLT" ]; then
- cryptsetup luksAddKey --key-slot "$SLT" --key-file \
- <(echo -n "$existing_key") "$DEV"
- else
- if [ $luks_type == "luks2" ]; then
- readarray -t usedSlotsBeforeAddKey < <(cryptsetup luksDump "${DEV}" \
- | sed -rn 's|^\s+([0-9]+): luks2$|\1|p')
- else
- readarray -t usedSlotsBeforeAddKey < <(cryptsetup luksDump "${DEV}" \
- | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p')
- fi
- cryptsetup luksAddKey --key-file <(echo -n "${existing_key}") "$DEV"
- fi < <(echo -n "${key}")
- if [ $? -ne 0 ]; then
- echo "Error while adding new key to LUKS header!" >&2
- exit 1
- fi
- #Determine slot used by new key if a desired slot was not specified
- if [ -z "$SLT" ]; then
- if [ "$luks_type" == "luks2" ]; then
- readarray -t usedSlotsAfterAddKey < <(cryptsetup luksDump "${DEV}" \
- | sed -rn 's|^\s+([0-9]+): luks2$|\1|p')
- else
- readarray -t usedSlotsAfterAddKey < <(cryptsetup luksDump "${DEV}" \
- | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p')
- fi
- for i in "${usedSlotsAfterAddKey[@]}"; do
- if [[ ! " ${usedSlotsBeforeAddKey[@]} " =~ " ${i} " ]]; then
- SLT=$i
- break
- fi
- done
- fi
- if [ -z "$SLT" ]; then
- echo "Error while adding new key to LUKS header! Key slot is undefined." >&2
- exit 1
- fi
- if [ "$luks_type" == "luks1" ]; then
- echo -n "$jwe" | luksmeta save -d "$DEV" -u "$UUID" -s "$SLT" 2>/dev/null
- else
- printf '{"type":"clevis","keyslots":["%s"],"jwe":%s}' "$SLT" "$(jose jwe fmt -i- <<< "$jwe")" \
- | cryptsetup token import "$DEV"
- fi
- if [ $? -ne 0 ]; then
- echo "Error while saving Clevis metadata in LUKSMeta!" >&2
- echo -n "$key" | cryptsetup luksRemoveKey "$DEV"
- exit 1
- fi
|