View file File name : clevis-luks-bind Content :#!/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/>. # . clevis-luks-common-functions 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 0 } function usage() { exec >&2 echo echo "Usage: clevis luks bind [-y] [-f] [-s SLT] [-k KEY] [-t TOKEN_ID] -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 " -y Automatically answer yes for all questions" echo echo " -s SLT The LUKS slot to use" echo echo " -t TKN_ID The LUKS token ID to use; only available for LUKS2" 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=() YES=() while getopts ":hfyd:s:k:t:" o; do case "$o" in f) FRC+=(-f);; d) DEV="$OPTARG";; s) SLT="$OPTARG";; k) KEY="$OPTARG";; t) TOKEN_ID="$OPTARG";; y) FRC+=(-f) YES+=(-y);; *) 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 elif ! EXE=$(command -v clevis-encrypt-"${PIN}") || [ -z "${EXE}" ]; then echo "'${PIN}' is not a valid 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" -a -n "$TOKEN_ID" ]; then echo "$DEV is a LUKS1 device; -t is only supported in LUKS2" exit 1 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' \ | sed -n 1p) 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 if ! key="$(clevis_luks_generate_key "${DEV}")" \ || [ -z "${key}" ]; then echo "Unable to generate key for ${DEV}" >&2 return 1 fi # Encrypt the new key jwe="$(echo -n "$key" | clevis encrypt "$PIN" "$CFG" "${YES}")" # 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 pbkdf_args="--pbkdf pbkdf2 --pbkdf-force-iterations 1000" if [ "$luks_type" == "luks1" ]; then pbkdf_args= # 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 ${pbkdf_args} --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 ${pbkdf_args} \ --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 $([ -n "$TOKEN_ID" ] && echo '--token-id '$TOKEN_ID) "$DEV" fi if [ $? -ne 0 ]; then echo "Error while saving Clevis metadata in LUKSMeta!" >&2 echo -n "$key" | cryptsetup luksRemoveKey "$DEV" exit 1 fi