Browse Source

Import upstream version 19

Sergio Arroutbi 1 year ago
parent
commit
e3cc8608ff
39 changed files with 712 additions and 108 deletions
  1. 61 0
      .github/workflows/build.yml
  2. 30 0
      .github/workflows/differential-shellcheck.yml
  3. 54 0
      .github/workflows/install-dependencies
  4. 43 0
      .gitignore
  5. 4 1
      README.md
  6. 1 1
      meson.build
  7. 7 2
      src/clevis
  8. 1 1
      src/clevis.1.adoc
  9. 4 1
      src/initramfs-tools/hooks/clevis.in
  10. 23 4
      src/luks/clevis-luks-bind
  11. 8 2
      src/luks/clevis-luks-bind.1.adoc
  12. 86 16
      src/luks/clevis-luks-common-functions.in
  13. 2 1
      src/luks/clevis-luks-edit
  14. 4 0
      src/luks/clevis-luks-unbind.in
  15. 10 3
      src/luks/clevis-luks-unlockers.7.adoc
  16. 9 0
      src/luks/meson.build
  17. 4 1
      src/luks/systemd/clevis-luks-askpass.in
  18. 14 0
      src/luks/systemd/dracut/clevis-pin-null/meson.build
  19. 28 0
      src/luks/systemd/dracut/clevis-pin-null/module-setup.sh.in
  20. 2 2
      src/luks/systemd/dracut/clevis-pin-tpm2/module-setup.sh.in
  21. 1 0
      src/luks/systemd/dracut/clevis/module-setup.sh.in
  22. 1 0
      src/luks/systemd/dracut/meson.build
  23. 55 0
      src/luks/tests/bind-luks1-avoid-luksmeta-corruption
  24. 74 0
      src/luks/tests/bind-luks2-ext-token
  25. 14 0
      src/luks/tests/meson.build
  26. 8 2
      src/luks/tests/tests-common-functions.in
  27. 15 2
      src/luks/tests/unbind-luks2
  28. 2 2
      src/pins/sss/clevis-decrypt-test
  29. 2 2
      src/pins/sss/clevis-encrypt-test
  30. 4 1
      src/pins/sss/meson.build
  31. 2 2
      src/pins/sss/pin-test
  32. 6 6
      src/pins/sss/pin-sss
  33. 3 3
      src/pins/sss/sss.c
  34. 1 0
      src/pins/sss/sss.h
  35. 3 3
      src/pins/tang/clevis-decrypt-tang
  36. 27 21
      src/pins/tpm2/clevis-decrypt-tpm2
  37. 52 19
      src/pins/tpm2/clevis-encrypt-tpm2
  38. 2 1
      src/pins/tpm2/meson.build
  39. 45 9
      src/pins/tpm2/pin-tpm2

+ 61 - 0
.github/workflows/build.yml

@@ -0,0 +1,61 @@
+---
+name: build
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    runs-on: ubuntu-18.04
+    continue-on-error: ${{ ! matrix.stable }}
+    strategy:
+      matrix:
+        os:
+          - fedora:latest
+          - quay.io/centos/centos:stream8
+          - quay.io/centos/centos:stream9
+          - debian:testing
+          - debian:latest
+          - ubuntu:rolling
+          - ubuntu:bionic
+        stable: [true]
+        include:
+          - os: fedora:rawhide
+            stable: false
+          - os: ubuntu:devel
+            stable: false
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Show OS information
+        run: cat /etc/os-release 2>/dev/null || echo /etc/os-release not available
+
+      - name: Install build dependencies
+        run: bash .github/workflows/install-dependencies
+
+      - name: Build clevis
+        run: |
+          mkdir -p build && cd build
+          export ninja=$(command -v ninja)
+          [ -z "${ninja}" ] && export ninja=$(command -v ninja-build)
+          export CFLAGS="-g -coverage"
+          meson .. || cat meson-logs/meson-log.txt >&2
+          ${ninja}
+
+      - name: Run tests
+        run: |
+          cd build
+          if ! meson test ; then
+              cat meson-logs/testlog.txt >&2
+              exit -1
+          fi
+
+      - name: Show full test logs
+        run: cat build/meson-logs/testlog.txt >&2
+
+    container:
+      image: ${{matrix.os}}
+      env:
+        DISTRO: ${{matrix.os}}
+      options: --privileged --device /dev/loop-control
+
+# vim:set ts=2 sw=2 et:

+ 30 - 0
.github/workflows/differential-shellcheck.yml

@@ -0,0 +1,30 @@
+# Doc: https://github.com/redhat-plumbers-in-action/differential-shellcheck#usage
+---
+
+name: Differential ShellCheck
+on:
+  pull_request:
+    branches: [master]
+
+permissions:
+  contents: read
+
+jobs:
+  lint:
+    runs-on: ubuntu-latest
+
+    permissions:
+      security-events: write
+      pull-requests: write
+
+    steps:
+      - name: Repository checkout
+        uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
+
+      - name: Differential ShellCheck
+        uses: redhat-plumbers-in-action/differential-shellcheck@v3
+        with:
+          severity: warning
+          token: ${{ secrets.GITHUB_TOKEN }}

+ 54 - 0
.github/workflows/install-dependencies

@@ -0,0 +1,54 @@
+#!/bin/bash -ex
+
+COMMON="meson curl git make file bzip2 jose tang cryptsetup keyutils jq socat ${CC}"
+
+case "${DISTRO}" in
+debian:*|ubuntu:*)
+    apt-get clean
+
+    while ! apt-get update; do
+        sleep 5
+    done
+
+    while ! apt-get -y \
+        -o Dpkg::Options::="--force-confdef" \
+        -o Dpkg::Options::="--force-confnew" \
+        dist-upgrade; do
+        sleep 5
+    done
+
+    export DEBIAN_FRONTEND=noninteractive
+    apt-get install -y keyboard-configuration console-setup
+
+    while ! apt-get -y install ${COMMON} \
+        build-essential pkg-config libssl-dev libjansson-dev libjose-dev \
+        luksmeta libluksmeta-dev libpwquality-tools libglib2.0-dev \
+        libudisks2-dev libaudit-dev systemd; do
+
+        sleep 5
+    done
+    ;;
+
+fedora:*)
+    printf 'max_parallel_downloads=10\nfastestmirror=1\n' >> /etc/dnf/dnf.conf
+    dnf -y clean all
+    dnf -y --setopt=deltarpm=0 update
+    dnf -y install dnf-utils jq socat cryptsetup keyutils
+    dnf -y builddep clevis
+    ;;
+
+*centos:*)
+    yum -y clean all
+    yum -y --setopt=deltarpm=0 update
+    yum install -y yum-utils
+    yum config-manager -y --set-enabled crb || yum config-manager \
+        -y --set-enabled powertools || :
+    yum -y install epel-release
+    yum -y --allowerasing install ${COMMON}
+    yum -y install pkgconfig openssl-devel openssl zlib-devel \
+        jansson-devel findutils gcc libjose-devel luksmeta libluksmeta-devel \
+        audit-libs-devel tpm2-tools desktop-file-utils cracklib-dicts
+    sed -i 's|>=1\.0\.2|>=1\.0\.1|' meson.build
+    ;;
+esac
+# vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:

+ 43 - 0
.gitignore

@@ -0,0 +1,43 @@
+*~
+*.a
+*.o
+*.la
+*.lo
+*.log
+*.m4
+*.so
+*.swp
+*.swo
+*.trs
+.autotools
+.cproject
+.deps
+.dirstamp
+.libs/
+.project
+.settings
+aclocal.m4
+ar-lib
+autom4te.cache
+build
+compile
+config.guess
+config.log
+config.status
+config.sub
+configure
+configure-stamp
+depcomp
+install-sh
+libtool
+ltmain.sh
+Makecache
+Makefile.in
+Makefile
+missing
+tags
+test-driver
+src/clevis-encrypt-sss
+src/clevis-decrypt-sss
+src/udisks2/clevis-luks-udisks2
+src/udisks2/clevis-luks-udisks2.desktop

+ 4 - 1
README.md

@@ -122,7 +122,7 @@ key. This key is added to LUKS as an additional passphrase. We then encrypt
 this key using Clevis, and store the output JWE inside the LUKS header using
 this key using Clevis, and store the output JWE inside the LUKS header using
 [LUKSMeta](http://github.com/latchset/luksmeta).
 [LUKSMeta](http://github.com/latchset/luksmeta).
 
 
-Here is an example where we bind `/dev/sda1` using the Tang ping:
+Here is an example where we bind `/dev/sda1` using the Tang pin:
 
 
 ```bash
 ```bash
 $ sudo clevis luks bind -d /dev/sda1 tang '{"url": "http://tang.local"}'
 $ sudo clevis luks bind -d /dev/sda1 tang '{"url": "http://tang.local"}'
@@ -136,6 +136,9 @@ Enter existing LUKS password:
 Upon successful completion of this binding process, the disk can be unlocked
 Upon successful completion of this binding process, the disk can be unlocked
 using one of the provided unlockers.
 using one of the provided unlockers.
 
 
+#### Network based unlocking
+If you want to use network based unlocking you will need to specify `rd.neednet=1` as kernel argument or use `--hostonly-cmdline` when creating dracut.
+
 #### Unlocker: Dracut
 #### Unlocker: Dracut
 
 
 The Dracut unlocker attempts to automatically unlock volumes during early
 The Dracut unlocker attempts to automatically unlock volumes during early

+ 1 - 1
meson.build

@@ -1,5 +1,5 @@
 project('clevis', 'c', license: 'GPL3+',
 project('clevis', 'c', license: 'GPL3+',
-  version: '18',
+  version: '19',
   default_options: 'c_std=c99'
   default_options: 'c_std=c99'
 )
 )
 
 

+ 7 - 2
src/clevis

@@ -27,6 +27,8 @@ function findexe() {
 }
 }
 
 
 cmd=clevis
 cmd=clevis
+input_commands="$cmd $@"
+
 while [ $# -gt 0 ]; do
 while [ $# -gt 0 ]; do
     [[ "$1" =~ ^- ]] && break
     [[ "$1" =~ ^- ]] && break
     cmd="$cmd-$1"
     cmd="$cmd-$1"
@@ -36,8 +38,11 @@ while [ $# -gt 0 ]; do
 done
 done
 
 
 exec >&2
 exec >&2
-echo
-echo "Command '$cmd' is invalid"
+if [ "$cmd" != "clevis" ];
+then
+    echo
+    echo "Command '$input_commands' is invalid"
+fi
 echo
 echo
 echo "Usage: clevis COMMAND [OPTIONS]"
 echo "Usage: clevis COMMAND [OPTIONS]"
 echo
 echo

+ 1 - 1
src/clevis.1.adoc

@@ -101,7 +101,7 @@ This is accomplished with a simple command:
 
 
 This command performs four steps:
 This command performs four steps:
 
 
-1. Creates a new key with the same entropy as the LUKS master key.
+1. Creates a new key with the same entropy as the LUKS master key -- maximum entropy bits is 256.
 2. Encrypts the new key with Clevis.
 2. Encrypts the new key with Clevis.
 3. Stores the Clevis JWE in the LUKS header.
 3. Stores the Clevis JWE in the LUKS header.
 4. Enables the new key for use with LUKS.
 4. Enables the new key for use with LUKS.

+ 4 - 1
src/initramfs-tools/hooks/clevis.in

@@ -42,7 +42,7 @@ die() {
 
 
 find_binary() {
 find_binary() {
     bin_name="$1"
     bin_name="$1"
-    resolved=$(which ${bin_name})
+    resolved=$(command -v ${bin_name})
     [ -z "$resolved" ] && die 1 "Unable to find ${bin_name}"
     [ -z "$resolved" ] && die 1 "Unable to find ${bin_name}"
     echo "$resolved"
     echo "$resolved"
 }
 }
@@ -58,6 +58,7 @@ fi
 
 
 copy_exec @bindir@/clevis-decrypt-tang || die 1 "@bindir@/clevis-decrypt-tang not found"
 copy_exec @bindir@/clevis-decrypt-tang || die 1 "@bindir@/clevis-decrypt-tang not found"
 copy_exec @bindir@/clevis-decrypt-sss || die 1 "@bindir@/clevis-decrypt-sss not found"
 copy_exec @bindir@/clevis-decrypt-sss || die 1 "@bindir@/clevis-decrypt-sss not found"
+copy_exec @bindir@/clevis-decrypt-null || die 1 "@bindir@/clevis-decrypt-null not found"
 copy_exec @bindir@/clevis-decrypt || die 1 "@bindir@/clevis-decrypt not found"
 copy_exec @bindir@/clevis-decrypt || die 1 "@bindir@/clevis-decrypt not found"
 copy_exec @bindir@/clevis-luks-common-functions || die 1 "@bindir@/clevis-luks-common-functions not found"
 copy_exec @bindir@/clevis-luks-common-functions || die 1 "@bindir@/clevis-luks-common-functions not found"
 copy_exec @bindir@/clevis-luks-list || die 1 "@bindir@/clevis-luks-list not found"
 copy_exec @bindir@/clevis-luks-list || die 1 "@bindir@/clevis-luks-list not found"
@@ -66,9 +67,11 @@ if [ -x @bindir@/clevis-decrypt-tpm2 ]; then
     tpm2_creatprimary_bin=$(find_binary "tpm2_createprimary")
     tpm2_creatprimary_bin=$(find_binary "tpm2_createprimary")
     tpm2_unseal_bin=$(find_binary "tpm2_unseal")
     tpm2_unseal_bin=$(find_binary "tpm2_unseal")
     tpm2_load_bin=$(find_binary "tpm2_load")
     tpm2_load_bin=$(find_binary "tpm2_load")
+    tpm2_flushcontext=$(find_binary "tpm2_flushcontext")
     copy_exec "${tpm2_creatprimary_bin}" || die 1 "Unable to copy ${tpm2_creatprimary_bin}" 
     copy_exec "${tpm2_creatprimary_bin}" || die 1 "Unable to copy ${tpm2_creatprimary_bin}" 
     copy_exec "${tpm2_unseal_bin}" || die 1 "Unable to copy ${tpm2_unseal_bin}"
     copy_exec "${tpm2_unseal_bin}" || die 1 "Unable to copy ${tpm2_unseal_bin}"
     copy_exec "${tpm2_load_bin}" || die 1 "Unable to copy ${tpm2_load_bin}"
     copy_exec "${tpm2_load_bin}" || die 1 "Unable to copy ${tpm2_load_bin}"
+    copy_exec "${tpm2_flushcontext}" || die 1 "Unable to copy ${tpm2_flushcontext}"
     for _LIBRARY in @libdir@/libtss2-tcti-device.so*; do
     for _LIBRARY in @libdir@/libtss2-tcti-device.so*; do
         if [ -e "${_LIBRARY}" ]; then
         if [ -e "${_LIBRARY}" ]; then
             copy_exec "${_LIBRARY}" || die 2 "Unable to copy ${_LIBRARY}"
             copy_exec "${_LIBRARY}" || die 2 "Unable to copy ${_LIBRARY}"

+ 23 - 4
src/luks/clevis-luks-bind

@@ -25,7 +25,7 @@ SUMMARY="Binds a LUKS device using the specified policy"
 usage() {
 usage() {
     exec >&2
     exec >&2
     echo
     echo
-    echo "Usage: clevis luks bind [-y] [-f] [-s SLT] [-k KEY] [-t TOKEN_ID] -d DEV PIN CFG"
+    echo "Usage: clevis luks bind [-y] [-f] [-s SLT] [-k KEY] [-t TOKEN_ID] [-e EXISTING_TOKEN_ID] -d DEV PIN CFG"
     echo
     echo
     echo "$SUMMARY":
     echo "$SUMMARY":
     echo
     echo
@@ -42,6 +42,8 @@ usage() {
     echo "  -k KEY       Non-interactively read LUKS password from KEY file"
     echo "  -k KEY       Non-interactively read LUKS password from KEY file"
     echo "  -k -         Non-interactively read LUKS password from standard input"
     echo "  -k -         Non-interactively read LUKS password from standard input"
     echo
     echo
+    echo "  -e E_TKN_ID  Existing LUKS token ID for existing passphrase; only available for LUKS2"
+    echo
     exit 2
     exit 2
 }
 }
 
 
@@ -52,13 +54,14 @@ fi
 
 
 FRC=
 FRC=
 YES=
 YES=
-while getopts ":hfyd:s:k:t:" o; do
+while getopts ":hfyd:s:k:t:e:" o; do
     case "$o" in
     case "$o" in
     f) FRC='-f';;
     f) FRC='-f';;
     d) DEV="$OPTARG";;
     d) DEV="$OPTARG";;
     s) SLT="$OPTARG";;
     s) SLT="$OPTARG";;
     k) KEY="$OPTARG";;
     k) KEY="$OPTARG";;
     t) TOKEN_ID="$OPTARG";;
     t) TOKEN_ID="$OPTARG";;
+    e) EXISTING_TOKEN_ID="$OPTARG";;
     y) FRC='-f'
     y) FRC='-f'
        YES='-y';;
        YES='-y';;
     *) usage;;
     *) usage;;
@@ -99,11 +102,20 @@ if [ "${luks_type}" = "luks1" ] && [ -n "${TOKEN_ID}" ]; then
     exit 1
     exit 1
 fi
 fi
 
 
+if [ -n "${EXISTING_TOKEN_ID}" ] && ! clevis_luks_luks2_existing_token_id_supported; then
+    echo "Existing token ID not supported in this cryptsetup version" >&2
+    exit 1
+fi
+
 # Get the existing passphrase/keyfile.
 # Get the existing passphrase/keyfile.
 existing_key=
 existing_key=
 keyfile=
 keyfile=
 case "${KEY}" in
 case "${KEY}" in
-"") IFS= read -r -s -p "Enter existing LUKS password: " existing_key; echo >&2;;
+ "")
+    if [ -z "${EXISTING_TOKEN_ID}" ] ; then
+        IFS= read -r -s -p "Enter existing LUKS password: " existing_key; echo >&2
+    fi
+    ;;
  -) IFS= read -r -s -p "" existing_key ||:
  -) IFS= read -r -s -p "" existing_key ||:
     if [ "${luks_type}" = "luks1" ] && ! luksmeta test -d "${DEV}" \
     if [ "${luks_type}" = "luks1" ] && ! luksmeta test -d "${DEV}" \
                                     && [ -z "${FRC}" ]; then
                                     && [ -z "${FRC}" ]; then
@@ -119,6 +131,13 @@ case "${KEY}" in
     ;;
     ;;
 esac
 esac
 
 
+# Check if existing token id for keyring read is provided
+# If so, keyfile is not allowed
+if [ -n "${EXISTING_TOKEN_ID}" ] && [ -n "${keyfile}" ] ; then
+    echo "Cannot specify kernel keyring description together with key file" >&2
+    exit 1
+fi
+
 # If necessary, initialize the LUKS volume.
 # If necessary, initialize the LUKS volume.
 if [ "${luks_type}" = "luks1" ] && ! luksmeta test -d "${DEV}"; then
 if [ "${luks_type}" = "luks1" ] && ! luksmeta test -d "${DEV}"; then
     luksmeta init -d "${DEV}" ${FRC}
     luksmeta init -d "${DEV}" ${FRC}
@@ -127,7 +146,7 @@ fi
 if ! clevis_luks_do_bind "${DEV}" "${SLT}" "${TOKEN_ID}" \
 if ! clevis_luks_do_bind "${DEV}" "${SLT}" "${TOKEN_ID}" \
                          "${PIN}" "${CFG}" \
                          "${PIN}" "${CFG}" \
                          "${YES}" "" \
                          "${YES}" "" \
-                         "${existing_key}" "${keyfile}"; then
+                         "${existing_key}" "${keyfile}" "${EXISTING_TOKEN_ID}"; then
     echo "Error adding new binding to ${DEV}" >&2
     echo "Error adding new binding to ${DEV}" >&2
     exit 1
     exit 1
 fi
 fi

+ 8 - 2
src/luks/clevis-luks-bind.1.adoc

@@ -9,7 +9,7 @@ clevis-luks-bind - Bind a LUKS device using the specified policy
 
 
 == SYNOPSIS
 == SYNOPSIS
 
 
-*clevis luks bind* [-f] [-y] -d DEV [-t TKN_ID] [-s SLT] [-k KEY] PIN CFG
+*clevis luks bind* [-f] [-y] -d DEV [-t TKN_ID] [-s SLT] [-k KEY] [-e EXISTING_TOKEN_ID] PIN CFG
 
 
 == OVERVIEW
 == OVERVIEW
 
 
@@ -20,7 +20,7 @@ policy. This is accomplished with a simple command:
 
 
 This command performs four steps:
 This command performs four steps:
 
 
-1. Creates a new key with the same entropy as the LUKS master key.
+1. Creates a new key with the same entropy as the LUKS master key -- maximum entropy bits is 256.
 2. Encrypts the new key with Clevis.
 2. Encrypts the new key with Clevis.
 3. Stores the Clevis JWE in the LUKS header.
 3. Stores the Clevis JWE in the LUKS header.
 4. Enables the new key for use with LUKS.
 4. Enables the new key for use with LUKS.
@@ -54,6 +54,12 @@ Clevis LUKS unlockers. See link:clevis-luks-unlockers.7.adoc[*clevis-luks-unlock
 * *-k* - :
 * *-k* - :
   Non-interactively read LUKS password from standard input
   Non-interactively read LUKS password from standard input
 
 
+* *-e* _E_TKN_ID_ :
+  LUKS token ID for existing passphrase; only available for LUKS2.
+  This parameter allows providing a configured token ID in LUKS2
+  containing the existing passphrase for this device, so that
+  existing passphrase is not prompted by clevis
+
 == CAVEATS
 == CAVEATS
 
 
 This command does not change the LUKS master key. This implies that if you
 This command does not change the LUKS master key. This implies that if you

+ 86 - 16
src/luks/clevis-luks-common-functions.in

@@ -20,6 +20,21 @@
 
 
 CLEVIS_UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
 CLEVIS_UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
 
 
+enable_debugging() {
+    # Automatically enable debugging if in initramfs phase and rd.debug
+    if [ -e /usr/lib/dracut-lib.sh ]; then
+        local bashopts=$-
+        # Because dracut is loosely written, disable hardening options temporarily
+        [[ $bashopts != *u* ]] || set +u
+        [[ $bashopts != *e* ]] || set +e
+        . /usr/lib/dracut-lib.sh
+        [[ $bashopts != *u* ]] || set -u
+        [[ $bashopts != *e* ]] || set -e
+    fi
+}
+
+enable_debugging
+
 # valid_slot() will check whether a given slot is possibly valid, i.e., if it
 # valid_slot() will check whether a given slot is possibly valid, i.e., if it
 # is a numeric value within the specified range.
 # is a numeric value within the specified range.
 valid_slot() {
 valid_slot() {
@@ -292,9 +307,10 @@ clevis_luks_check_valid_key_or_keyfile() {
     local KEY="${2:-}"
     local KEY="${2:-}"
     local KEYFILE="${3:-}"
     local KEYFILE="${3:-}"
     local SLT="${4:-}"
     local SLT="${4:-}"
+    local EXISTING_TOKEN_ID="${5:-}"
 
 
     [ -z "${DEV}" ] && return 1
     [ -z "${DEV}" ] && return 1
-    [ -z "${KEYFILE}" ] && [ -z "${KEY}" ] && return 1
+    [ -z "${EXISTING_TOKEN_ID}" ] && [ -z "${KEYFILE}" ] && [ -z "${KEY}" ] && return 1
 
 
     local extra_args
     local extra_args
     extra_args="$([ -n "${SLT}" ] && printf -- '--key-slot %s' "${SLT}")"
     extra_args="$([ -n "${SLT}" ] && printf -- '--key-slot %s' "${SLT}")"
@@ -303,6 +319,11 @@ clevis_luks_check_valid_key_or_keyfile() {
                    ${extra_args}
                    ${extra_args}
         return
         return
     fi
     fi
+    if [ -n "${EXISTING_TOKEN_ID}" ]; then
+        cryptsetup open --test-passphrase "${DEV}" --token-id "${EXISTING_TOKEN_ID}" \
+                   ${extra_args}
+        return
+    fi
 
 
     printf '%s' "${KEY}" | cryptsetup open --test-passphrase "${DEV}" \
     printf '%s' "${KEY}" | cryptsetup open --test-passphrase "${DEV}" \
                                       ${extra_args}
                                       ${extra_args}
@@ -313,6 +334,7 @@ clevis_luks_check_valid_key_or_keyfile() {
 clevis_luks_unlock_device_by_slot() {
 clevis_luks_unlock_device_by_slot() {
     local DEV="${1}"
     local DEV="${1}"
     local SLT="${2}"
     local SLT="${2}"
+    local SKIP_CHECK="${3}"
 
 
     [ -z "${DEV}" ] && return 1
     [ -z "${DEV}" ] && return 1
     [ -z "${SLT}" ] && return 1
     [ -z "${SLT}" ] && return 1
@@ -327,8 +349,9 @@ clevis_luks_unlock_device_by_slot() {
                        || [ -z "${passphrase}" ]; then
                        || [ -z "${passphrase}" ]; then
         return 1
         return 1
     fi
     fi
-
-    clevis_luks_check_valid_key_or_keyfile "${DEV}" "${passphrase}" || return 1
+    if [ -z "${SKIP_CHECK}" ]; then
+        clevis_luks_check_valid_key_or_keyfile "${DEV}" "${passphrase}" || return 1
+    fi
     printf '%s' "${passphrase}"
     printf '%s' "${passphrase}"
 }
 }
 
 
@@ -336,6 +359,8 @@ clevis_luks_unlock_device_by_slot() {
 # parameter and returns the decoded passphrase.
 # parameter and returns the decoded passphrase.
 clevis_luks_unlock_device() {
 clevis_luks_unlock_device() {
     local DEV="${1}"
     local DEV="${1}"
+    local SKIP_CHECK="YES"
+
     [ -z "${DEV}" ] && return 1
     [ -z "${DEV}" ] && return 1
 
 
     local used_slots
     local used_slots
@@ -346,7 +371,7 @@ clevis_luks_unlock_device() {
 
 
     local slt pt
     local slt pt
     for slt in ${used_slots}; do
     for slt in ${used_slots}; do
-        if ! pt=$(clevis_luks_unlock_device_by_slot "${DEV}" "${slt}") \
+        if ! pt=$(clevis_luks_unlock_device_by_slot "${DEV}" "${slt}" "${SKIP_CHECK}") \
                   || [ -z "${pt}" ]; then
                   || [ -z "${pt}" ]; then
              continue
              continue
         fi
         fi
@@ -393,13 +418,18 @@ clevis_devices_to_unlock() {
     clevis_devices=
     clevis_devices=
 
 
     # Build list of devices to unlock.
     # Build list of devices to unlock.
-    while read -r _ crypt_device _; do
+    while read -r _volname_ crypt_device _; do
+        # skip empty lines and lines which begin with the '#' char, per
+        # crypttab(5)
+        case $_volname_ in
+            ''|\#*) continue ;;
+        esac
         if ! dev=$(clevis_map_device "${crypt_device}") \
         if ! dev=$(clevis_map_device "${crypt_device}") \
                    || [ -z "${dev}" ]; then
                    || [ -z "${dev}" ]; then
             # Unable to get the device - maybe it's not available, e.g. a
             # Unable to get the device - maybe it's not available, e.g. a
             # device on a volume group that has not been activated yet.
             # device on a volume group that has not been activated yet.
             # Add it to the list anyway, since it's a pending device.
             # Add it to the list anyway, since it's a pending device.
-            clevis_devices="${clevis_devices} ${dev}"
+            clevis_devices="${clevis_devices} ${crypt_device}"
             continue
             continue
         fi
         fi
 
 
@@ -448,6 +478,12 @@ clevis_luks1_save_slot() {
         echo "Error saving metadata to LUKSMeta slot ${SLOT} from ${DEV}" >&2
         echo "Error saving metadata to LUKSMeta slot ${SLOT} from ${DEV}" >&2
         return 1
         return 1
     fi
     fi
+
+    if ! luksmeta test -d "${DEV}" 2>/dev/null >/dev/null ; then
+        echo "Error detected after saving metadata to LUKSMeta slot ${SLOT}, device ${DEV}" >&2
+        return 1
+    fi
+
     return 0
     return 0
 }
 }
 
 
@@ -734,10 +770,11 @@ clevis_luks_add_key() {
     local NEWKEY="${3}"
     local NEWKEY="${3}"
     local KEY="${4}"
     local KEY="${4}"
     local KEYFILE="${5:-}"
     local KEYFILE="${5:-}"
+    local EXISTING_TOKEN_ID="${6:-}"
 
 
     [ -z "${DEV}" ] && return 1
     [ -z "${DEV}" ] && return 1
     [ -z "${NEWKEY}" ] && return 1
     [ -z "${NEWKEY}" ] && return 1
-    [ -z "${KEY}" ] && [ -z "${KEYFILE}" ] && return 1
+    [ -z "${EXISTING_TOKEN_ID}" ] && [ -z "${KEY}" ] && [ -z "${KEYFILE}" ] && return 1
 
 
     local extra_args='' input
     local extra_args='' input
     input="$(printf '%s\n%s' "${KEY}" "${NEWKEY}")"
     input="$(printf '%s\n%s' "${KEY}" "${NEWKEY}")"
@@ -745,10 +782,16 @@ clevis_luks_add_key() {
         extra_args="$(printf -- '--key-file %s' "${KEYFILE}")"
         extra_args="$(printf -- '--key-file %s' "${KEYFILE}")"
         input="$(printf '%s' "${NEWKEY}")"
         input="$(printf '%s' "${NEWKEY}")"
     fi
     fi
+    if [ -n "${EXISTING_TOKEN_ID}" ]; then
+        extra_args="$(printf -- '--token-id %s' "${EXISTING_TOKEN_ID}")"
+        input="$(printf '%s' "${NEWKEY}")"
+    fi
+    local pbkdf_args="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
 
 
     printf '%s' "${input}" | cryptsetup luksAddKey --batch-mode \
     printf '%s' "${input}" | cryptsetup luksAddKey --batch-mode \
                                          --key-slot "${SLT}" \
                                          --key-slot "${SLT}" \
                                          "${DEV}" \
                                          "${DEV}" \
+                                         ${pbkdf_args} \
                                          ${extra_args}
                                          ${extra_args}
 }
 }
 
 
@@ -759,6 +802,7 @@ clevis_luks_update_key() {
     local NEWKEY="${3}"
     local NEWKEY="${3}"
     local KEY="${4}"
     local KEY="${4}"
     local KEYFILE="${5:-}"
     local KEYFILE="${5:-}"
+    local EXISTING_TOKEN_ID="${6:-}"
 
 
     [ -z "${DEV}" ] && return 1
     [ -z "${DEV}" ] && return 1
     [ -z "${NEWKEY}" ] && return 1
     [ -z "${NEWKEY}" ] && return 1
@@ -768,7 +812,7 @@ clevis_luks_update_key() {
     local in_place
     local in_place
     clevis_luks_check_valid_key_or_keyfile "${DEV}" \
     clevis_luks_check_valid_key_or_keyfile "${DEV}" \
                                            "${KEY}" "${KEYFILE}" \
                                            "${KEY}" "${KEYFILE}" \
-                                           "${SLT}" 2>/dev/null \
+                                           "${SLT}" "${EXISTING_TOKEN_ID}" 2>/dev/null \
                                            && in_place=true
                                            && in_place=true
 
 
     local input extra_args=
     local input extra_args=
@@ -777,11 +821,19 @@ clevis_luks_update_key() {
         extra_args="$(printf -- '--key-file %s' "${KEYFILE}")"
         extra_args="$(printf -- '--key-file %s' "${KEYFILE}")"
         input="$(printf '%s' "${NEWKEY}")"
         input="$(printf '%s' "${NEWKEY}")"
     fi
     fi
+    if [ -n "${EXISTING_TOKEN_ID}" ]; then
+        extra_args="$(printf -- '--token-id %s' "${EXISTING_TOKEN_ID}")"
+        input="$(printf '%s' "${NEWKEY}")"
+    fi
+
+    local pbkdf_args="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
 
 
     if [ -n "${in_place}" ]; then
     if [ -n "${in_place}" ]; then
         printf '%s' "${input}" | cryptsetup luksChangeKey "${DEV}" \
         printf '%s' "${input}" | cryptsetup luksChangeKey "${DEV}" \
                                             --key-slot "${SLT}" \
                                             --key-slot "${SLT}" \
-                                            --batch-mode ${extra_args}
+                                            --batch-mode \
+                                            ${pbkdf_args} \
+                                            ${extra_args}
         return
         return
     fi
     fi
 
 
@@ -803,6 +855,7 @@ clevis_luks_save_key_to_slot() {
     local KEY="${4}"
     local KEY="${4}"
     local KEYFILE="${5:-}"
     local KEYFILE="${5:-}"
     local OVERWRITE="${6:-}"
     local OVERWRITE="${6:-}"
+    local EXISTING_TOKEN_ID="${7:-}"
 
 
     [ -z "${DEV}" ] && return 1
     [ -z "${DEV}" ] && return 1
     [ -z "${SLT}" ] && return 1
     [ -z "${SLT}" ] && return 1
@@ -820,13 +873,13 @@ clevis_luks_save_key_to_slot() {
         [ -n "${OVERWRITE}" ] || return 1
         [ -n "${OVERWRITE}" ] || return 1
 
 
         clevis_luks_update_key "${DEV}" "${SLT}" \
         clevis_luks_update_key "${DEV}" "${SLT}" \
-                               "${NEWKEY}" "${KEY}" "${KEYFILE}"
+                               "${NEWKEY}" "${KEY}" "${KEYFILE}" "${EXISTING_TOKEN_ID}"
         return
         return
     fi
     fi
 
 
     # Add a new key.
     # Add a new key.
     clevis_luks_add_key "${DEV}" "${SLT}" \
     clevis_luks_add_key "${DEV}" "${SLT}" \
-                        "${NEWKEY}" "${KEY}" "${KEYFILE}"
+                        "${NEWKEY}" "${KEY}" "${KEYFILE}" "${EXISTING_TOKEN_ID}"
 }
 }
 
 
 # clevis_luks_generate_key() generates a new key for use with clevis.
 # clevis_luks_generate_key() generates a new key for use with clevis.
@@ -835,6 +888,7 @@ clevis_luks_generate_key() {
     [ -z "${DEV}" ] && return 1
     [ -z "${DEV}" ] && return 1
 
 
     local dump filter bits
     local dump filter bits
+    local MAX_ENTROPY_BITS=256  # Maximum allowed by pwmake.
     dump=$(cryptsetup luksDump "${DEV}")
     dump=$(cryptsetup luksDump "${DEV}")
     if cryptsetup isLuks --type luks1 "${DEV}"; then
     if cryptsetup isLuks --type luks1 "${DEV}"; then
         filter="$(echo "${dump}" | sed -rn 's|MK bits:[ \t]*([0-9]+)|\1|p')"
         filter="$(echo "${dump}" | sed -rn 's|MK bits:[ \t]*([0-9]+)|\1|p')"
@@ -846,6 +900,9 @@ clevis_luks_generate_key() {
     fi
     fi
 
 
     bits="$(echo -n "${filter}" | sort -n | tail -n 1)"
     bits="$(echo -n "${filter}" | sort -n | tail -n 1)"
+    if [ "${bits}" -gt "${MAX_ENTROPY_BITS}" ]; then
+        bits="${MAX_ENTROPY_BITS}"
+    fi
     pwmake "${bits}"
     pwmake "${bits}"
 }
 }
 
 
@@ -918,15 +975,17 @@ clevis_luks_do_bind() {
     local OVERWRITE="${7:-}"
     local OVERWRITE="${7:-}"
     local KEY="${8:-}"
     local KEY="${8:-}"
     local KEYFILE="${9:-}"
     local KEYFILE="${9:-}"
+    local EXISTING_TOKEN_ID="${10:-}"
 
 
     [ -z "${DEV}" ] && return 1
     [ -z "${DEV}" ] && return 1
     [ -z "${PIN}" ] && return 1
     [ -z "${PIN}" ] && return 1
     [ -z "${CFG}" ] && return 1
     [ -z "${CFG}" ] && return 1
 
 
-
     if ! clevis_luks_check_valid_key_or_keyfile "${DEV}" \
     if ! clevis_luks_check_valid_key_or_keyfile "${DEV}" \
                                                 "${KEY}" \
                                                 "${KEY}" \
                                                 "${KEYFILE}" \
                                                 "${KEYFILE}" \
+                                                "" \
+                                                "${EXISTING_TOKEN_ID}" \
                     && ! KEY="$(clevis_luks_get_existing_key "${DEV}" \
                     && ! KEY="$(clevis_luks_get_existing_key "${DEV}" \
                                 "Enter existing LUKS password: " \
                                 "Enter existing LUKS password: " \
                                 "recover")"; then
                                 "recover")"; then
@@ -941,12 +1000,16 @@ clevis_luks_do_bind() {
     fi
     fi
 
 
     # Encrypt the new key.
     # Encrypt the new key.
-    jwe="$(printf '%s' "${newkey}" | clevis encrypt "${PIN}" "${CFG}" ${YES})"
+    if ! jwe="$(printf '%s' "${newkey}" | clevis encrypt "${PIN}" "${CFG}" \
+                ${YES})" || [ -z "${jwe}" ]; then
+        echo "Unable to perform encryption with PIN '${PIN}' and config '${CFG}'" >&2
+        return 1
+    fi
 
 
     # We can proceed to binding, after backing up the LUKS header and
     # We can proceed to binding, after backing up the LUKS header and
     # metadata.
     # metadata.
     local CLEVIS_TMP_DIR
     local CLEVIS_TMP_DIR
-
+    mkdir -p "${TMPDIR:-/tmp}"
     if ! CLEVIS_TMP_DIR="$(mktemp -d)" || [ -z "${CLEVIS_TMP_DIR}" ]; then
     if ! CLEVIS_TMP_DIR="$(mktemp -d)" || [ -z "${CLEVIS_TMP_DIR}" ]; then
         echo "Unable to create a a temporary dir for device backup/restore" >&2
         echo "Unable to create a a temporary dir for device backup/restore" >&2
         return 1
         return 1
@@ -971,7 +1034,7 @@ clevis_luks_do_bind() {
 
 
     if ! clevis_luks_save_key_to_slot "${DEV}" "${SLT}" \
     if ! clevis_luks_save_key_to_slot "${DEV}" "${SLT}" \
                                       "${newkey}" "${KEY}" "${KEYFILE}" \
                                       "${newkey}" "${KEY}" "${KEYFILE}" \
-                                      "${OVERWRITE}"; then
+                                      "${OVERWRITE}" "${EXISTING_TOKEN_ID}"; then
         echo "Unable to save/update key slot; operation cancelled" >&2
         echo "Unable to save/update key slot; operation cancelled" >&2
         clevis_luks_restore_dev "${CLEVIS_TMP_DIR}" || :
         clevis_luks_restore_dev "${CLEVIS_TMP_DIR}" || :
         rm -rf "${CLEVIS_TMP_DIR}"
         rm -rf "${CLEVIS_TMP_DIR}"
@@ -992,12 +1055,19 @@ clevis_luks_do_bind() {
 }
 }
 
 
 # clevis_luks_luks2_supported() indicates whether we support LUKS2 devices.
 # clevis_luks_luks2_supported() indicates whether we support LUKS2 devices.
-# Suppor is determined at build time.
+# Support is determined at build time.
 function clevis_luks_luks2_supported() {
 function clevis_luks_luks2_supported() {
     # We require cryptsetup >= 2.0.4 to fully support LUKSv2.
     # We require cryptsetup >= 2.0.4 to fully support LUKSv2.
     return @OLD_CRYPTSETUP@
     return @OLD_CRYPTSETUP@
 }
 }
 
 
+# clevis_luks_luks2_existing_token_id_supported() indicates whether
+# cryptsetup allows token id for passphrase providing
+function clevis_luks_luks2_existing_token_id_supported() {
+    # We require cryptsetup >= 2.6.0 to fully support LUKSv2 addkey/open by token ID
+    return @OLD_CRYPTSETUP_EXISTING_TOKEN_ID@
+}
+
 # clevis_luks_type() returns the LUKS type of a device, e.g. "luks1".
 # clevis_luks_type() returns the LUKS type of a device, e.g. "luks1".
 clevis_luks_type() {
 clevis_luks_type() {
     local DEV="${1}"
     local DEV="${1}"

+ 2 - 1
src/luks/clevis-luks-edit

@@ -119,6 +119,7 @@ if ! pretty_cfg="$(printf '%s' "${cfg}" | jq --monochrome-output .)" \
     exit 1
     exit 1
 fi
 fi
 
 
+mkdir -p "${TMPDIR:-/tmp}"
 if ! CLEVIS_EDIT_TMP="$(mktemp -d)" || [ -z "${CLEVIS_EDIT_TMP}" ]; then
 if ! CLEVIS_EDIT_TMP="$(mktemp -d)" || [ -z "${CLEVIS_EDIT_TMP}" ]; then
     echo "Creating a temporary dir for editing binding failed" >&2
     echo "Creating a temporary dir for editing binding failed" >&2
     exit 1
     exit 1
@@ -173,7 +174,7 @@ rm -rf "${CLEVIS_EDIT_TMP}"
 
 
 echo "Updating binding..."
 echo "Updating binding..."
 if ! clevis_luks_do_bind "${DEV}" "${SLT}" "" "${pin}" "${new_cfg}" \
 if ! clevis_luks_do_bind "${DEV}" "${SLT}" "" "${pin}" "${new_cfg}" \
-                         "-y" "overwrite" 2>/dev/null; then
+                         "-y" "overwrite"; then
     echo "Unable to update binding in ${DEV}:${SLT}. Operation cancelled." >&2
     echo "Unable to update binding in ${DEV}:${SLT}. Operation cancelled." >&2
     exit 1
     exit 1
 fi
 fi

+ 4 - 0
src/luks/clevis-luks-unbind.in

@@ -106,6 +106,10 @@ elif [ "$luks_type" == "luks2" ]; then
     grep -q "^\s*$SLT: luks2" <<< "$dump" && KILL=true
     grep -q "^\s*$SLT: luks2" <<< "$dump" && KILL=true
     TOK="$(grep -E -B1 "^\s+Keyslot:\s+$SLT$" <<< "$dump" \
     TOK="$(grep -E -B1 "^\s+Keyslot:\s+$SLT$" <<< "$dump" \
         | sed -rn 's|^\s+([0-9]+): clevis|\1|p')"
         | sed -rn 's|^\s+([0-9]+): clevis|\1|p')"
+    if [ -z "${TOK}" ]; then
+        echo "No clevis slot detected on device ${DEV}:${SLT}!" >&2
+        exit 1
+    fi
 fi
 fi
 
 
 if [ -z "${FRC[*]}" ]; then
 if [ -z "${FRC[*]}" ]; then

+ 10 - 3
src/luks/clevis-luks-unlockers.7.adoc

@@ -37,9 +37,16 @@ Once Clevis is integrated into your initramfs, a simple reboot should unlock
 your root volume. Note, however, that early boot integration only works for the
 your root volume. Note, however, that early boot integration only works for the
 root volume. Non-root volumes should use the late boot unlocker.
 root volume. Non-root volumes should use the late boot unlocker.
 
 
-Dracut will bring up your network using DHCP by default. If you need to specify
-additional network parameters, such as static IP configuration, please consult
-the dracut documentation.
+Dracut will not bring up your network by default. You can either have it come
+up via DHCP by using rd.neednet=1 in kernel cmdline or you can specify custom
+network parameters, such as static IP configuration, please consult the dracut
+documentation.
+
+DHCP can be easily added to early boot by setting it in a configuration file
+and rebuilding initramfs afterwards
+
+    $ echo 'kernel_cmdline="rd.neednet=1"' | sudo tee /etc/dracut.conf.d/clevis.conf
+    $ sudo dracut -f
 
 
 == LATE BOOT UNLOCKING
 == LATE BOOT UNLOCKING
 
 

+ 9 - 0
src/luks/meson.build

@@ -14,6 +14,15 @@ else
     endif
     endif
 endif
 endif
 
 
+libcryptsetup_ext_token_id = dependency('libcryptsetup', version: '>=2.6.0', required: false)
+if libcryptsetup_ext_token_id.found()
+    luksmeta_data.set('OLD_CRYPTSETUP_EXISTING_TOKEN_ID', '0')
+    message('cryptsetup version supports existing token id')
+else
+    luksmeta_data.set('OLD_CRYPTSETUP_EXISTING_TOKEN_ID', '1')
+    warning('cryptsetup version does not support existing token id')
+endif
+
 clevis_luks_common_functions = configure_file(
 clevis_luks_common_functions = configure_file(
   input: 'clevis-luks-common-functions.in',
   input: 'clevis-luks-common-functions.in',
   output: 'clevis-luks-common-functions',
   output: 'clevis-luks-common-functions',

+ 4 - 1
src/luks/systemd/clevis-luks-askpass.in

@@ -67,8 +67,11 @@ while true; do
     done
     done
 
 
     [ "${loop}" != true ] && break
     [ "${loop}" != true ] && break
+
     # Checking for pending devices to be unlocked.
     # Checking for pending devices to be unlocked.
-    if remaining=$(clevis_devices_to_unlock) && [ -z "${remaining}" ]; then
+    remaining_crypttab=$(clevis_devices_to_unlock) ||:
+    remaining_askfiles=$(ls "${path}"/ask.* 2>/dev/null) ||:
+    if [ -z "${remaining_crypttab}" ] && [ -z "${remaining_askfiles}" ]; then
         break;
         break;
     fi
     fi
 
 

+ 14 - 0
src/luks/systemd/dracut/clevis-pin-null/meson.build

@@ -0,0 +1,14 @@
+dracut = dependency('dracut', required: false)
+
+if dracut.found()
+  dracutdir = dracut.get_pkgconfig_variable('dracutmodulesdir') + '/60' + meson.project_name() + '-pin-null'
+
+  configure_file(
+    input: 'module-setup.sh.in',
+    output: 'module-setup.sh',
+    install_dir: dracutdir,
+    configuration: data,
+  )
+else
+  warning('Will not install dracut module clevis-pin-null due to missing dependencies!')
+endif

+ 28 - 0
src/luks/systemd/dracut/clevis-pin-null/module-setup.sh.in

@@ -0,0 +1,28 @@
+#!/bin/bash
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2016 Red Hat, Inc.
+# 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/>.
+#
+
+depends() {
+    echo clevis
+    return 0
+}
+
+install() {
+    inst clevis-decrypt-null
+}

+ 2 - 2
src/luks/systemd/dracut/clevis-pin-tpm2/module-setup.sh.in

@@ -19,7 +19,7 @@
 #
 #
 
 
 check() {
 check() {
-    require_binaries clevis-decrypt-tpm2 tpm2_createprimary tpm2_unseal tpm2_load || return 1
+    require_binaries clevis-decrypt-tpm2 tpm2_createprimary tpm2_flushcontext tpm2_load tpm2_unseal || return 1
     require_any_binary tpm2_pcrread tpm2_pcrlist || return 1
     require_any_binary tpm2_pcrread tpm2_pcrlist || return 1
     return 0
     return 0
 }
 }
@@ -30,7 +30,7 @@ depends() {
 }
 }
 
 
 install() {
 install() {
-    inst_multiple clevis-decrypt-tpm2 tpm2_createprimary tpm2_unseal tpm2_load
+    inst_multiple clevis-decrypt-tpm2 tpm2_createprimary tpm2_flushcontext tpm2_load tpm2_unseal
     inst_multiple -o tpm2_pcrread tpm2_pcrlist
     inst_multiple -o tpm2_pcrread tpm2_pcrlist
     inst_libdir_file "libtss2-tcti-device.so*"
     inst_libdir_file "libtss2-tcti-device.so*"
 }
 }

+ 1 - 0
src/luks/systemd/dracut/clevis/module-setup.sh.in

@@ -46,6 +46,7 @@ install() {
         luksmeta \
         luksmeta \
         clevis \
         clevis \
         mktemp \
         mktemp \
+        mkdir \
         jose
         jose
 
 
     dracut_need_initqueue
     dracut_need_initqueue

+ 1 - 0
src/luks/systemd/dracut/meson.build

@@ -2,3 +2,4 @@ subdir('clevis')
 subdir('clevis-pin-tang')
 subdir('clevis-pin-tang')
 subdir('clevis-pin-tpm2')
 subdir('clevis-pin-tpm2')
 subdir('clevis-pin-sss')
 subdir('clevis-pin-sss')
+subdir('clevis-pin-null')

+ 55 - 0
src/luks/tests/bind-luks1-avoid-luksmeta-corruption

@@ -0,0 +1,55 @@
+#!/bin/bash -ex
+#
+# Copyright (c) 2021 Red Hat, Inc.
+# Author: Sergio Arroutbi Braojos <sarroutb@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/>.
+#
+
+TEST=$(basename "${0}")
+. tests-common-functions
+
+on_exit() {
+    [ ! -d "${TMP}" ] && return 0
+    tang_stop "${TMP}"
+    rm -rf "${TMP}"
+}
+
+trap 'on_exit' EXIT
+
+TMP="$(mktemp -d)"
+
+ADV_AMOUNT=50
+
+# Create LUKS1 device
+DEV="${TMP}/luks1-device"
+new_device "luks1" "${DEV}"
+
+# TANG server specifics
+port=$(tang_new_random_port)
+tang_run "${TMP}" "${port}"
+url="http://localhost:${port}"
+
+# Initial binding to ensure luksmeta gets corrupted
+for ADV_NU in $(seq 0 ${ADV_AMOUNT}); do
+    "${TANGD_KEYGEN}" "${TMP}/db"
+done
+tang_new_keys "${TMP}"
+
+CFG=$(printf '{"url":"%s"}' "${url}")
+
+# At this point, luks bind must return an error. If not, test fails
+if clevis luks bind -y -d "${DEV}" tang "${CFG}" <<< "${DEFAULT_PASS}"; then
+    error "${TEST}: Binding is expected to fail when given a too long adv"
+fi

+ 74 - 0
src/luks/tests/bind-luks2-ext-token

@@ -0,0 +1,74 @@
+#!/bin/bash -ex
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2022 Red Hat, Inc.
+# Author: Sergio Arroutbi <sarroutb@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/>.
+#
+
+TEST=$(basename "${0}")
+. tests-common-functions
+
+on_exit() {
+    [ -d "${TMP}" ] && rm -rf "${TMP}"
+}
+
+create_existing_token_id_from_keyring() {
+    local DEV="${1}"
+    local KEYDESC="${2}"
+    local TOKEN_ID="${3}"
+    local PASS="${4}"
+    if [[ -z "${DEV}" ]] || [[ -z "${KEYDESC}" ]] || [[ -z "${TOKEN_ID}" ]]; then
+        return 1
+    fi
+    KEYRING_ID=$(keyctl add user "${KEYDESC}" "${PASS}" @s)
+    keyctl print "${KEYRING_ID}" 2>/dev/null 1>/dev/null
+    cryptsetup token add --token-id "${TOKEN_ID}" --key-description "${KEYDESC}" "${DEV}"
+}
+
+if ! luks2_supported; then
+    skip_test "${TEST}: LUKS2 is not supported."
+fi
+
+if  ! luks2_existing_token_id_supported; then
+    skip_test "${TEST}: Existing token ID not supported"
+fi
+
+trap 'on_exit' EXIT
+trap 'exit' ERR
+
+TMP="$(mktemp -d)"
+
+ADV="${TMP}/adv.jws"
+tang_create_adv "${TMP}" "${ADV}"
+CFG="$(printf '{"url":"foobar","adv":"%s"}' "$ADV")"
+
+EXISTING_TOKEN_ID=5
+KEYDESC="testkey"
+PASS="123exttokenid_"
+DEV="${TMP}/luks2-device-ext-token"
+new_device "luks2" "${DEV}" "${PASS}"
+
+create_existing_token_id_from_keyring "${DEV}" "${KEYDESC}" "${EXISTING_TOKEN_ID}" "${PASS}"
+
+if ! clevis luks bind -y -d "${DEV}" -e "${EXISTING_TOKEN_ID}" tang "${CFG}"; then
+    error "${TEST}: Binding expected to succeed with existing token id:${EXISTING_TOKEN_ID}" >&2
+fi
+
+KEYFILE="${TMP}/keyfile.txt"
+touch "${KEYFILE}"
+if clevis luks bind -y -d "${DEV}" -e "${EXISTING_TOKEN_ID}" -k "${KEYFILE}" tang "${CFG}"; then
+    error "${TEST}: Using existing token id and keyfile should dump an error" >&2
+fi

+ 14 - 0
src/luks/tests/meson.build

@@ -5,6 +5,15 @@ jq = find_program('jq', required: false)
 # given token slot.
 # given token slot.
 cryptsetup = find_program('cryptsetup', required: true)
 cryptsetup = find_program('cryptsetup', required: true)
 
 
+# Use keyctl to check an existing token id can be created from
+# kernel keyring password
+keyutils = find_program('keyctl', required: false)
+if keyutils.found()
+    message('keyutils installed')
+else
+    warning('keyutils not installed, unable to test existing token id binding')
+endif
+
 common_functions = configure_file(input: 'tests-common-functions.in',
 common_functions = configure_file(input: 'tests-common-functions.in',
   output: 'tests-common-functions',
   output: 'tests-common-functions',
   configuration: luksmeta_data,
   configuration: luksmeta_data,
@@ -59,6 +68,7 @@ test('edit-tang-luks1', find_program('edit-tang-luks1'), env: env, timeout: 150)
 
 
 test('backup-restore-luks1', find_program('backup-restore-luks1'), env: env, timeout: 60)
 test('backup-restore-luks1', find_program('backup-restore-luks1'), env: env, timeout: 60)
 test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env, timeout: 60)
 test('pass-tang-luks1', find_program('pass-tang-luks1'), env: env, timeout: 60)
+test('bind-luks1-avoid-luksmeta-corruption', find_program('bind-luks1-avoid-luksmeta-corruption'), env: env, timeout: 60)
 
 
 # LUKS2 tests go here, and they get included if we get support for it, based
 # LUKS2 tests go here, and they get included if we get support for it, based
 # on the cryptsetup version.
 # on the cryptsetup version.
@@ -69,6 +79,10 @@ if luksmeta_data.get('OLD_CRYPTSETUP') == '0'
   test('unbind-unbound-slot-luks2', find_program('unbind-unbound-slot-luks2'), env: env)
   test('unbind-unbound-slot-luks2', find_program('unbind-unbound-slot-luks2'), env: env)
   test('unbind-luks2', find_program('unbind-luks2'), env: env, timeout: 60)
   test('unbind-luks2', find_program('unbind-luks2'), env: env, timeout: 60)
 
 
+  if keyutils.found() and luksmeta_data.get('OLD_CRYPTSETUP_EXISTING_TOKEN_ID') == '0'
+    test('bind-luks2-ext-token', find_program('bind-luks2-ext-token'), env: env, timeout: 60)
+  endif
+
   if jq.found()
   if jq.found()
     test('list-recursive-luks2', find_program('list-recursive-luks2'), env: env, timeout: 60)
     test('list-recursive-luks2', find_program('list-recursive-luks2'), env: env, timeout: 60)
     test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60)
     test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60)

+ 8 - 2
src/luks/tests/tests-common-functions.in

@@ -36,6 +36,12 @@ luks2_supported() {
     return @OLD_CRYPTSETUP@
     return @OLD_CRYPTSETUP@
 }
 }
 
 
+# We require cryptsetup >= 2.6.0 to fully support LUKSv2 addkey/open by token ID
+# Support is determined at build time.
+luks2_existing_token_id_supported() {
+    return @OLD_CRYPTSETUP_EXISTING_TOKEN_ID@
+}
+
 # Creates a new LUKS1 or LUKS2 device to be used.
 # Creates a new LUKS1 or LUKS2 device to be used.
 new_device() {
 new_device() {
     local LUKS="${1}"
     local LUKS="${1}"
@@ -64,7 +70,7 @@ new_device() {
 
 
     fallocate -l64M "${DEV}"
     fallocate -l64M "${DEV}"
     cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \
     cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \
-        --pbkdf-force-iterations 1000 --batch-mode \
+        --pbkdf-force-iterations 1000 --key-size 512 --batch-mode \
         --force-password "${DEV}" <<< "${PASS}"
         --force-password "${DEV}" <<< "${PASS}"
     # Caching the just-formatted device for possible reuse.
     # Caching the just-formatted device for possible reuse.
     cp -f "${DEV}" "${DEV_CACHED}"
     cp -f "${DEV}" "${DEV_CACHED}"
@@ -88,7 +94,7 @@ new_device_keyfile() {
 
 
     fallocate -l64M "${DEV}"
     fallocate -l64M "${DEV}"
     cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \
     cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \
-        --pbkdf-force-iterations 1000 --batch-mode \
+        --pbkdf-force-iterations 1000 --key-size 512 --batch-mode \
         "${DEV}" "${KEYFILE}"
         "${DEV}" "${KEYFILE}"
 }
 }
 
 

+ 15 - 2
src/luks/tests/unbind-luks2

@@ -42,10 +42,23 @@ DEV="${TMP}/luks2-device"
 new_device "luks2" "${DEV}"
 new_device "luks2" "${DEV}"
 # Binding.
 # Binding.
 if ! clevis luks bind -d "${DEV}" tang "${CFG}" <<< "${DEFAULT_PASS}"; then
 if ! clevis luks bind -d "${DEV}" tang "${CFG}" <<< "${DEFAULT_PASS}"; then
-    error "${TEST}: Binding is expected to succeed." >&2
+    error "${TEST}: Binding is expected to succeed."
 fi
 fi
 
 
 SLT=1
 SLT=1
 if ! clevis luks unbind -f -d "${DEV}" -s "${SLT}"; then
 if ! clevis luks unbind -f -d "${DEV}" -s "${SLT}"; then
-    error "${TEST}: Unbind is expected to succeed for device ${DEV} and slot ${SLT}" >&2
+    error "${TEST}: Unbind is expected to succeed for device ${DEV} and slot ${SLT}"
+fi
+
+SLT=0
+if ! echo "${DEFAULT_PASS}" | cryptsetup open --test-passphrase "${DEV}" --key-slot "${SLT}"; then
+   error "${TEST}: Unable to open device ${DEV}:${SLT}"
+fi
+
+if clevis luks unbind -f -d "${DEV}" -s "${SLT}"; then
+   error "${TEST}: Unbind is expected to fail for device ${DEV}:${SLT} that is not bound with clevis"
+fi
+
+if ! echo "${DEFAULT_PASS}" | cryptsetup open --test-passphrase "${DEV}" --key-slot "${SLT}"; then
+  error "${TEST}: Unbind is expected not to remove non clevis slots"
 fi
 fi

+ 2 - 2
src/pins/sss/clevis-decrypt-test

@@ -22,11 +22,11 @@
 
 
 read -r -d . hdr
 read -r -d . hdr
 
 
-if [ "$(jose fmt -q "$hdr" -SyOg clevis -g pin -u-)" != "test" ]; then
+if [ "$(jose fmt -q "$hdr" -SyOg clevis -g pin -u-)" != "null" ]; then
     echo "JWE pin mismatch!" >&2
     echo "JWE pin mismatch!" >&2
     exit 1
     exit 1
 fi
 fi
 
 
-jwk="$(jose fmt -q "$hdr" -SyOg clevis -g test -g jwk -Oo-)" || exit 1
+jwk="$(jose fmt -q "$hdr" -SyOg clevis -g null -g jwk -Oo-)" || exit 1
 
 
 exec jose jwe dec -k- -i- < <(echo -n "$jwk$hdr."; /bin/cat)
 exec jose jwe dec -k- -i- < <(echo -n "$jwk$hdr."; /bin/cat)

+ 2 - 2
src/pins/sss/clevis-encrypt-test

@@ -26,10 +26,10 @@ if ! cfg="$(jose fmt -j "$1" -Oo- 2>/dev/null)"; then
 fi
 fi
 
 
 jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"
 jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"
-jwe='{"protected":{"clevis":{"pin":"test","test":{}}}}'
+jwe='{"protected":{"clevis":{"pin":"null","null":{}}}}'
 
 
 if ! jose fmt -j "$cfg" -g fail -T; then
 if ! jose fmt -j "$cfg" -g fail -T; then
-    jwe="$(jose fmt -j "$jwe" -Og protected -g clevis -g test -j "$jwk" -Os jwk -UUUUo-)"
+    jwe="$(jose fmt -j "$jwe" -Og protected -g clevis -g null -j "$jwk" -Os jwk -UUUUo-)"
 fi
 fi
 
 
 exec jose jwe enc -i- -k- -I- -c < <(echo -n "$jwe$jwk"; /bin/cat)
 exec jose jwe enc -i- -k- -I- -c < <(echo -n "$jwe$jwk"; /bin/cat)

+ 4 - 1
src/pins/sss/meson.build

@@ -28,8 +28,11 @@ if jansson.found() and libcrypto.found()
     separator: ':'
     separator: ':'
   )
   )
 
 
+  bins += join_paths(meson.current_source_dir(), 'clevis-encrypt-null')
+  bins += join_paths(meson.current_source_dir(), 'clevis-decrypt-null')
+
   test('pin-sss', find_program(join_paths(src, 'pin-sss')), env: env)
   test('pin-sss', find_program(join_paths(src, 'pin-sss')), env: env)
-  test('pin-test', find_program(join_paths(src, 'pin-test')), env: env)
+  test('pin-null', find_program(join_paths(src, 'pin-null')), env: env)
 else
 else
   warning('Will not install sss pin due to missing dependencies!')
   warning('Will not install sss pin due to missing dependencies!')
 endif
 endif

+ 2 - 2
src/pins/sss/pin-test

@@ -2,9 +2,9 @@
 
 
 trap 'exit' ERR
 trap 'exit' ERR
 
 
-e="$(echo -n hi | clevis encrypt test '{}')"
+e="$(echo -n hi | clevis encrypt null '{}')"
 d="$(echo -n "$e" | clevis decrypt)"
 d="$(echo -n "$e" | clevis decrypt)"
 test "$d" == "hi"
 test "$d" == "hi"
 
 
-e="$(echo -n hi | clevis encrypt test '{"fail":true}')"
+e="$(echo -n hi | clevis encrypt null '{"fail":true}')"
 ! echo "$e" | clevis decrypt
 ! echo "$e" | clevis decrypt

+ 6 - 6
src/pins/sss/pin-sss

@@ -1,24 +1,24 @@
 #!/bin/bash -ex
 #!/bin/bash -ex
 
 
-e="$(echo hi | clevis encrypt sss '{"t":1,"pins":{"test":[{},{}]}}')"
+e="$(echo hi | clevis encrypt sss '{"t":1,"pins":{"null":[{},{}]}}')"
 d="$(echo "$e" | clevis decrypt)"
 d="$(echo "$e" | clevis decrypt)"
 test "$d" == "hi"
 test "$d" == "hi"
 
 
-e="$(echo hi | clevis encrypt sss '{"t":1,"pins":{"test":[{},{"fail":true}]}}')"
+e="$(echo hi | clevis encrypt sss '{"t":1,"pins":{"null":[{},{"fail":true}]}}')"
 d="$(echo "$e" | clevis decrypt)"
 d="$(echo "$e" | clevis decrypt)"
 test "$d" == "hi"
 test "$d" == "hi"
 
 
-e="$(echo hi | clevis encrypt sss '{"t":1,"pins":{"test":[{"fail":true},{"fail":true}]}}')"
+e="$(echo hi | clevis encrypt sss '{"t":1,"pins":{"null":[{"fail":true},{"fail":true}]}}')"
 ! echo "$e" | clevis decrypt
 ! echo "$e" | clevis decrypt
 
 
-e="$(echo hi | clevis encrypt sss '{"t":2,"pins":{"test":[{},{}]}}')"
+e="$(echo hi | clevis encrypt sss '{"t":2,"pins":{"null":[{},{}]}}')"
 d="$(echo "$e" | clevis decrypt)"
 d="$(echo "$e" | clevis decrypt)"
 test "$d" == "hi"
 test "$d" == "hi"
 
 
-e="$(echo hi | clevis encrypt sss '{"t":2,"pins":{"test":[{},{"fail":true}]}}')"
+e="$(echo hi | clevis encrypt sss '{"t":2,"pins":{"null":[{},{"fail":true}]}}')"
 ! echo "$e" | clevis decrypt
 ! echo "$e" | clevis decrypt
 
 
-e="$(echo hi | clevis encrypt sss '{"t":2,"pins":{"test":[{"fail":true},{"fail":true}]}}')"
+e="$(echo hi | clevis encrypt sss '{"t":2,"pins":{"null":[{"fail":true},{"fail":true}]}}')"
 ! echo "$e" | clevis decrypt
 ! echo "$e" | clevis decrypt
 
 
 ! e="$(echo hi | clevis encrypt sss '{"t":1,"pins":{"tang":[{"url":"foo bar"}]}}')"
 ! e="$(echo hi | clevis encrypt sss '{"t":1,"pins":{"tang":[{"url":"foo bar"}]}}')"

+ 3 - 3
src/pins/sss/sss.c

@@ -214,7 +214,7 @@ sss_point(const json_t *sss, size_t *len)
     if (BN_rand_range(xx, pp) <= 0)
     if (BN_rand_range(xx, pp) <= 0)
         return NULL;
         return NULL;
 
 
-    if (BN_zero(yy) <= 0)
+    if (BN_set_word(yy, 0) <= 0)
         return NULL;
         return NULL;
 
 
     for (size_t i = 0; i < json_array_size(e); i++) {
     for (size_t i = 0; i < json_array_size(e); i++) {
@@ -272,7 +272,7 @@ sss_recover(const json_t *p, size_t npnts, const uint8_t *pnts[])
     if (!ctx || !pp || !acc || !tmp || !k)
     if (!ctx || !pp || !acc || !tmp || !k)
         return NULL;
         return NULL;
 
 
-    if (BN_zero(k) <= 0)
+    if (BN_set_word(k, 0) <= 0)
         return NULL;
         return NULL;
 
 
     len = jose_b64_dec(p, NULL, 0);
     len = jose_b64_dec(p, NULL, 0);
@@ -303,7 +303,7 @@ sss_recover(const json_t *p, size_t npnts, const uint8_t *pnts[])
 
 
             /* acc *= (0 - xi) / (xo - xi) */
             /* acc *= (0 - xi) / (xo - xi) */
 
 
-            if (BN_zero(tmp) <= 0)
+            if (BN_set_word(tmp, 0) <= 0)
                 return NULL;
                 return NULL;
 
 
             if (BN_mod_sub(tmp, tmp, xi, pp, ctx) <= 0)
             if (BN_mod_sub(tmp, tmp, xi, pp, ctx) <= 0)

+ 1 - 0
src/pins/sss/sss.h

@@ -20,6 +20,7 @@
 #pragma once
 #pragma once
 #include <jansson.h>
 #include <jansson.h>
 #include <stdint.h>
 #include <stdint.h>
+#include <sys/types.h>
 
 
 json_t *
 json_t *
 sss_generate(size_t key_bytes, size_t threshold);
 sss_generate(size_t key_bytes, size_t threshold);

+ 3 - 3
src/pins/tang/clevis-decrypt-tang

@@ -99,10 +99,10 @@ fi
 
 
 xfr="$(jose jwk exc -i '{"alg":"ECMR"}' -l- -r- <<< "$clt$eph")"
 xfr="$(jose jwk exc -i '{"alg":"ECMR"}' -l- -r- <<< "$clt$eph")"
 
 
-url="$url/rec/$kid"
+rec_url="$url/rec/$kid"
 ct="Content-Type: application/jwk+json"
 ct="Content-Type: application/jwk+json"
-if ! rep="$(curl -sfg -X POST -H "$ct" --data-binary @- "$url" <<< "$xfr")"; then
-    echo "Error communicating with the server!" >&2
+if ! rep="$(curl -sfg -X POST -H "$ct" --data-binary @- "$rec_url" <<< "$xfr")"; then
+    echo "Error communicating with server $url" >&2
     exit 1
     exit 1
 fi
 fi
 
 

+ 27 - 21
src/pins/tpm2/clevis-decrypt-tpm2

@@ -54,26 +54,28 @@ if [[ $TPM2TOOLS_VERSION -lt 3 ]] || [[ $TPM2TOOLS_VERSION -gt 5 ]]; then
     exit 1
     exit 1
 fi
 fi
 
 
-# Old environment variables for tpm2-tools 3.0
-export TPM2TOOLS_TCTI_NAME=device
-export TPM2TOOLS_DEVICE_FILE=
-for dev in /dev/tpmrm?; do
-    [ -e "$dev" ] || continue
-    TPM2TOOLS_DEVICE_FILE="$dev"
-    break
-done
-
-# New environment variable for tpm2-tools >= 3.1
-export TPM2TOOLS_TCTI="$TPM2TOOLS_TCTI_NAME:$TPM2TOOLS_DEVICE_FILE"
-
-if [ -z "$TPM2TOOLS_DEVICE_FILE" ]; then
-    echo "A TPM2 device with the in-kernel resource manager is needed!" >&2
-    exit 1
-fi
+if [ -z "$TPM2TOOLS_TCTI" ]; then
+    # Old environment variables for tpm2-tools 3.0
+    export TPM2TOOLS_TCTI_NAME=device
+    export TPM2TOOLS_DEVICE_FILE=
+    for dev in /dev/tpmrm?; do
+        [ -e "$dev" ] || continue
+        TPM2TOOLS_DEVICE_FILE="$dev"
+        break
+    done
+
+    # New environment variable for tpm2-tools >= 3.1
+    export TPM2TOOLS_TCTI="$TPM2TOOLS_TCTI_NAME:$TPM2TOOLS_DEVICE_FILE"
+
+    if [ -z "$TPM2TOOLS_DEVICE_FILE" ]; then
+        echo "A TPM2 device with the in-kernel resource manager is needed!" >&2
+        exit 1
+    fi
 
 
-if ! [[ -r "$TPM2TOOLS_DEVICE_FILE" && -w "$TPM2TOOLS_DEVICE_FILE" ]]; then
-    echo "The $TPM2TOOLS_DEVICE_FILE device must be readable and writable!" >&2
-    exit 1
+    if ! [[ -r "$TPM2TOOLS_DEVICE_FILE" && -w "$TPM2TOOLS_DEVICE_FILE" ]]; then
+        echo "The $TPM2TOOLS_DEVICE_FILE device must be readable and writable!" >&2
+        exit 1
+    fi
 fi
 fi
 
 
 read -r -d . hdr
 read -r -d . hdr
@@ -99,15 +101,16 @@ if ! key="$(jose fmt -j- -Og clevis -g tpm2 -g key -Su- <<< "$jhd")"; then
 fi
 fi
 
 
 if ! jwk_pub="$(jose fmt -j- -Og clevis -g tpm2 -g jwk_pub -Su- <<< "$jhd")"; then
 if ! jwk_pub="$(jose fmt -j- -Og clevis -g tpm2 -g jwk_pub -Su- <<< "$jhd")"; then
-    echo "JWE missing required 'key' header parameter!" >&2
+    echo "JWE missing required 'jwk_pub' header parameter!" >&2
     exit 1
     exit 1
 fi
 fi
 
 
 if ! jwk_priv="$(jose fmt -j- -Og clevis -g tpm2 -g jwk_priv -Su- <<< "$jhd")"; then
 if ! jwk_priv="$(jose fmt -j- -Og clevis -g tpm2 -g jwk_priv -Su- <<< "$jhd")"; then
-    echo "JWE missing required 'key' header parameter!" >&2
+    echo "JWE missing required 'jwk_priv' header parameter!" >&2
     exit 1
     exit 1
 fi
 fi
 
 
+mkdir -p "${TMPDIR:-/tmp}"
 if ! TMP="$(mktemp -d)"; then
 if ! TMP="$(mktemp -d)"; then
     echo "Creating a temporary dir for TPM files failed!" >&2
     echo "Creating a temporary dir for TPM files failed!" >&2
     exit 1
     exit 1
@@ -142,6 +145,7 @@ if [ -n "$fail" ]; then
     echo "Creating TPM2 primary key failed!" >&2
     echo "Creating TPM2 primary key failed!" >&2
     exit 1
     exit 1
 fi
 fi
+tpm2_flushcontext -t
 
 
 case "$TPM2TOOLS_VERSION" in
 case "$TPM2TOOLS_VERSION" in
     3) tpm2_load -Q -c "$TMP"/primary.context -u "$TMP"/jwk.pub -r "$TMP"/jwk.priv \
     3) tpm2_load -Q -c "$TMP"/primary.context -u "$TMP"/jwk.pub -r "$TMP"/jwk.priv \
@@ -154,6 +158,7 @@ if [ -n "$fail" ]; then
     echo "Loading jwk to TPM2 failed!" >&2
     echo "Loading jwk to TPM2 failed!" >&2
     exit 1
     exit 1
 fi
 fi
+tpm2_flushcontext -t
 
 
 case "$TPM2TOOLS_VERSION" in
 case "$TPM2TOOLS_VERSION" in
     3) jwk="$(tpm2_unseal -c "$TMP"/load.context ${pcr_spec:+-L $pcr_spec})" || fail=$?;;
     3) jwk="$(tpm2_unseal -c "$TMP"/load.context ${pcr_spec:+-L $pcr_spec})" || fail=$?;;
@@ -164,6 +169,7 @@ if [ -n "$fail" ]; then
     echo "Unsealing jwk from TPM failed!" >&2
     echo "Unsealing jwk from TPM failed!" >&2
     exit 1
     exit 1
 fi
 fi
+tpm2_flushcontext -t
 
 
 (echo -n "$jwk$hdr."; /bin/cat) | jose jwe dec -k- -i-
 (echo -n "$jwk$hdr."; /bin/cat) | jose jwe dec -k- -i-
 exit $?
 exit $?

+ 52 - 19
src/pins/tpm2/clevis-encrypt-tpm2

@@ -67,6 +67,27 @@ if [ -t 0 ]; then
     exit 2
     exit 2
 fi
 fi
 
 
+validate_pcrs() {
+    local _tpm2_tools_v="${1}"
+    local _pcr_bank="${2}"
+    local _pcrs="${3}"
+    [ -z "${_pcr_bank}" ] && return 1
+    [ -z "${_pcrs}" ] && return 0
+
+    local _fail=
+    local _pcrs_r=
+    case "${_tpm2_tools_v}" in
+    3) _pcrs_r="$(tpm2_pcrlist -L "${_pcr_bank}":"${_pcrs}" | grep -v "^${_pcr_bank}")" || _fail=$?;;
+    4|5) _pcrs_r=$(tpm2_pcrread "${_pcr_bank}":"${_pcrs}" | grep -v "  ${_pcr_bank}")  || _fail=$?;;
+    *) _fail=1
+    esac
+
+    if [ -n "${_fail}" ] || [ -z "${_pcrs_r}" ]; then
+        return 1
+    fi
+    return 0
+}
+
 TPM2TOOLS_INFO="$(tpm2_createprimary -v)"
 TPM2TOOLS_INFO="$(tpm2_createprimary -v)"
 
 
 match='version="(.)\.'
 match='version="(.)\.'
@@ -76,26 +97,28 @@ if [[ $TPM2TOOLS_VERSION -lt 3 ]] || [[ $TPM2TOOLS_VERSION -gt 5 ]]; then
     exit 1
     exit 1
 fi
 fi
 
 
-# Old environment variables for tpm2-tools 3.0
-export TPM2TOOLS_TCTI_NAME=device
-export TPM2TOOLS_DEVICE_FILE=
-for dev in /dev/tpmrm?; do
-    [ -e "$dev" ] || continue
-    TPM2TOOLS_DEVICE_FILE="$dev"
-    break
-done
-
-# New environment variable for tpm2-tools >= 3.1
-export TPM2TOOLS_TCTI="$TPM2TOOLS_TCTI_NAME:$TPM2TOOLS_DEVICE_FILE"
-
-if [ -z "$TPM2TOOLS_DEVICE_FILE" ]; then
-    echo "A TPM2 device with the in-kernel resource manager is needed!" >&2
-    exit 1
-fi
+if [ -z "$TPM2TOOLS_TCTI" ]; then
+    # Old environment variables for tpm2-tools 3.0
+    export TPM2TOOLS_TCTI_NAME=device
+    export TPM2TOOLS_DEVICE_FILE=
+    for dev in /dev/tpmrm?; do
+        [ -e "$dev" ] || continue
+        TPM2TOOLS_DEVICE_FILE="$dev"
+        break
+    done
+
+    # New environment variable for tpm2-tools >= 3.1
+    export TPM2TOOLS_TCTI="$TPM2TOOLS_TCTI_NAME:$TPM2TOOLS_DEVICE_FILE"
+
+    if [ -z "$TPM2TOOLS_DEVICE_FILE" ]; then
+        echo "A TPM2 device with the in-kernel resource manager is needed!" >&2
+        exit 1
+    fi
 
 
-if ! [[ -r "$TPM2TOOLS_DEVICE_FILE" && -w "$TPM2TOOLS_DEVICE_FILE" ]]; then
-    echo "The $TPM2TOOLS_DEVICE_FILE device must be readable and writable!" >&2
-    exit 1
+    if ! [[ -r "$TPM2TOOLS_DEVICE_FILE" && -w "$TPM2TOOLS_DEVICE_FILE" ]]; then
+        echo "The $TPM2TOOLS_DEVICE_FILE device must be readable and writable!" >&2
+        exit 1
+    fi
 fi
 fi
 
 
 if ! cfg="$(jose fmt -j "$1" -Oo- 2>/dev/null)"; then
 if ! cfg="$(jose fmt -j "$1" -Oo- 2>/dev/null)"; then
@@ -137,6 +160,11 @@ if jose fmt -j- -Og pcr_ids 2>/dev/null <<< "${pcr_cfg}" \
     fi
     fi
 fi
 fi
 
 
+if ! validate_pcrs "${TPM2TOOLS_VERSION}" "${pcr_bank}" "${pcr_ids}"; then
+    echo "Unable to validate combination of PCR bank '${pcr_bank}' and PCR IDs '${pcr_ids}'." >&2
+    exit 1
+fi
+
 pcr_digest="$(jose fmt -j- -Og pcr_digest -u- <<< "$cfg")" || true
 pcr_digest="$(jose fmt -j- -Og pcr_digest -u- <<< "$cfg")" || true
 
 
 if ! jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"; then
 if ! jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"; then
@@ -144,6 +172,7 @@ if ! jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"; then
     exit 1
     exit 1
 fi
 fi
 
 
+mkdir -p "${TMPDIR:-/tmp}"
 if ! TMP="$(mktemp -d)"; then
 if ! TMP="$(mktemp -d)"; then
     echo "Creating a temporary dir for TPM files failed!" >&2
     echo "Creating a temporary dir for TPM files failed!" >&2
     exit 1
     exit 1
@@ -160,6 +189,7 @@ if [ -n "$fail" ]; then
     echo "Creating TPM2 primary key failed!" >&2
     echo "Creating TPM2 primary key failed!" >&2
     exit 1
     exit 1
 fi
 fi
+tpm2_flushcontext -t
 
 
 policy_options=()
 policy_options=()
 if [ -n "$pcr_ids" ]; then
 if [ -n "$pcr_ids" ]; then
@@ -173,6 +203,7 @@ if [ -n "$pcr_ids" ]; then
             echo "Creating PCR hashes file failed!" >&2
             echo "Creating PCR hashes file failed!" >&2
             exit 1
             exit 1
         fi
         fi
+        tpm2_flushcontext -t
     else
     else
         if ! jose b64 dec -i- -O "$TMP"/pcr.digest <<< "$pcr_digest"; then
         if ! jose b64 dec -i- -O "$TMP"/pcr.digest <<< "$pcr_digest"; then
             echo "Error decoding PCR digest!" >&2
             echo "Error decoding PCR digest!" >&2
@@ -191,6 +222,7 @@ if [ -n "$pcr_ids" ]; then
         echo "create policy fail, please check the environment or parameters!"
         echo "create policy fail, please check the environment or parameters!"
         exit 1
         exit 1
     fi
     fi
+    tpm2_flushcontext -t
 
 
     policy_options+=(-L "$TMP/pcr.policy")
     policy_options+=(-L "$TMP/pcr.policy")
 else
 else
@@ -208,6 +240,7 @@ if [ -n "$fail" ]; then
     echo "Creating TPM2 object for jwk failed!" >&2
     echo "Creating TPM2 object for jwk failed!" >&2
     exit 1
     exit 1
 fi
 fi
+tpm2_flushcontext -t
 
 
 if ! jwk_pub="$(jose b64 enc -I "$TMP"/jwk.pub)"; then
 if ! jwk_pub="$(jose b64 enc -I "$TMP"/jwk.pub)"; then
     echo "Encoding jwk.pub in Base64 failed!" >&2
     echo "Encoding jwk.pub in Base64 failed!" >&2

+ 2 - 1
src/pins/tpm2/meson.build

@@ -1,5 +1,6 @@
 cmds = ['tpm2_createprimary', ['tpm2_pcrread', 'tpm2_pcrlist'],
 cmds = ['tpm2_createprimary', ['tpm2_pcrread', 'tpm2_pcrlist'],
-        'tpm2_createpolicy', 'tpm2_create', 'tpm2_load', 'tpm2_unseal']
+        'tpm2_createpolicy', 'tpm2_create', 'tpm2_flushcontext', 'tpm2_load',
+        'tpm2_unseal']
 
 
 all = true
 all = true
 foreach cmd : cmds
 foreach cmd : cmds

+ 45 - 9
src/pins/tpm2/pin-tpm2

@@ -46,6 +46,35 @@ tpm2_available() {
         echo "The ${TPM2TOOLS_DEVICE_FILE} device must be readable and writable!" >&2
         echo "The ${TPM2TOOLS_DEVICE_FILE} device must be readable and writable!" >&2
         return 1
         return 1
     fi
     fi
+
+    local _tpm2tools_info="$(tpm2_createprimary -v)"
+    local _match='version="(.)\.'
+    [[ ${_tpm2tools_info} =~ ${_match} ]] && TPM2TOOLS_VERSION="${BASH_REMATCH[1]}"
+    if [[ $TPM2TOOLS_VERSION -lt 3 ]] || [[ $TPM2TOOLS_VERSION -gt 5 ]]; then
+        echo "The tpm2 pin requires a tpm2-tools version between 3 and 5" >&2
+        return 1
+    fi
+    export TPM2TOOLS_VERSION
+}
+
+validate_pcrs() {
+    local _pcr_bank="${1}"
+    local _pcrs="${2}"
+    [ -z "${_pcr_bank}" ] && return 1
+    [ -z "${_pcrs}" ] && return 0
+
+    local _fail=
+    local _pcrs_r=
+    case "${TPM2TOOLS_VERSION}" in
+    3) _pcrs_r="$(tpm2_pcrlist -L "${_pcr_bank}":"${_pcrs}" | grep -v "^${_pcr_bank}")" || _fail=$?;;
+    4|5) _pcrs_r=$(tpm2_pcrread "${_pcr_bank}":"${_pcrs}" | grep -v "  ${_pcr_bank}")  || _fail=$?;;
+    *) _fail=1
+    esac
+
+    if [ -n "${_fail}" ] || [ -z "${_pcrs_r}" ]; then
+        return 1
+    fi
+    return 0
 }
 }
 
 
 # Checking if we can run this test.
 # Checking if we can run this test.
@@ -111,15 +140,22 @@ test_pcr_ids "${orig}" '{"key": "ecc"}' "" || exit 1
 
 
 # Issue #103: now let's try a few different configs with both strings and
 # Issue #103: now let's try a few different configs with both strings and
 # arrays and check if we get the expected pcr_ids.
 # arrays and check if we get the expected pcr_ids.
-test_pcr_ids "${orig}" '{"pcr_ids": "16"}' "16" || exit 1
-test_pcr_ids "${orig}" '{"pcr_ids": ["16"]}' "16"  || exit 1
-test_pcr_ids "${orig}" '{"pcr_ids": "4,  16"}' "4,16" || exit 1
-test_pcr_ids "${orig}" '{"pcr_ids": "4,16"}' "4,16" || exit 1
-test_pcr_ids "${orig}" '{"pcr_ids": ["4,16"]}' "4,16" || exit 1
-test_pcr_ids "${orig}" '{"pcr_ids": [4,16]}' "4,16" || exit 1
-test_pcr_ids "${orig}" '{"pcr_ids": [4,  16]}' "4,16" || exit 1
-test_pcr_ids "${orig}" '{"pcr_ids": ["4","16"]}' "4,16" || exit 1
-! test_pcr_ids "${orig}" '{"pcr_ids": ["4","16"]}' "foo bar" || exit 1
+
+# Let's first make sure this would be a valid configuration.
+_default_pcr_bank="sha1"
+if validate_pcrs "${_default_pcr_bank}" "4,16"; then
+    test_pcr_ids "${orig}" '{"pcr_ids": "16"}' "16" || exit 1
+    test_pcr_ids "${orig}" '{"pcr_ids": ["16"]}' "16"  || exit 1
+    test_pcr_ids "${orig}" '{"pcr_ids": "4,  16"}' "4,16" || exit 1
+    test_pcr_ids "${orig}" '{"pcr_ids": "4,16"}' "4,16" || exit 1
+    test_pcr_ids "${orig}" '{"pcr_ids": ["4,16"]}' "4,16" || exit 1
+    test_pcr_ids "${orig}" '{"pcr_ids": [4,16]}' "4,16" || exit 1
+    test_pcr_ids "${orig}" '{"pcr_ids": [4,  16]}' "4,16" || exit 1
+    test_pcr_ids "${orig}" '{"pcr_ids": ["4","16"]}' "4,16" || exit 1
+    ! test_pcr_ids "${orig}" '{"pcr_ids": ["4","16"]}' "foo bar" || exit 1
+else
+    echo "Skipping tests related to issue#103 because the combination of pcr_bank and PCRs is invalid" >&2
+fi
 
 
 # Test with policies if we have the PIN rewrite available
 # Test with policies if we have the PIN rewrite available
 if ! $(which clevis-pin-tpm2 >/dev/null 2>&1);
 if ! $(which clevis-pin-tpm2 >/dev/null 2>&1);