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
 [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
 $ 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
 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
 
 The Dracut unlocker attempts to automatically unlock volumes during early

+ 1 - 1
meson.build

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

+ 7 - 2
src/clevis

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

+ 1 - 1
src/clevis.1.adoc

@@ -101,7 +101,7 @@ This is accomplished with a simple command:
 
 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.
 3. Stores the Clevis JWE in the LUKS header.
 4. Enables the new key for use with LUKS.

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

@@ -42,7 +42,7 @@ die() {
 
 find_binary() {
     bin_name="$1"
-    resolved=$(which ${bin_name})
+    resolved=$(command -v ${bin_name})
     [ -z "$resolved" ] && die 1 "Unable to find ${bin_name}"
     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-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-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"
@@ -66,9 +67,11 @@ if [ -x @bindir@/clevis-decrypt-tpm2 ]; then
     tpm2_creatprimary_bin=$(find_binary "tpm2_createprimary")
     tpm2_unseal_bin=$(find_binary "tpm2_unseal")
     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_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_flushcontext}" || die 1 "Unable to copy ${tpm2_flushcontext}"
     for _LIBRARY in @libdir@/libtss2-tcti-device.so*; do
         if [ -e "${_LIBRARY}" ]; then
             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() {
     exec >&2
     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 "$SUMMARY":
     echo
@@ -42,6 +42,8 @@ usage() {
     echo "  -k KEY       Non-interactively read LUKS password from KEY file"
     echo "  -k -         Non-interactively read LUKS password from standard input"
     echo
+    echo "  -e E_TKN_ID  Existing LUKS token ID for existing passphrase; only available for LUKS2"
+    echo
     exit 2
 }
 
@@ -52,13 +54,14 @@ fi
 
 FRC=
 YES=
-while getopts ":hfyd:s:k:t:" o; do
+while getopts ":hfyd:s:k:t:e:" o; do
     case "$o" in
     f) FRC='-f';;
     d) DEV="$OPTARG";;
     s) SLT="$OPTARG";;
     k) KEY="$OPTARG";;
     t) TOKEN_ID="$OPTARG";;
+    e) EXISTING_TOKEN_ID="$OPTARG";;
     y) FRC='-f'
        YES='-y';;
     *) usage;;
@@ -99,11 +102,20 @@ if [ "${luks_type}" = "luks1" ] && [ -n "${TOKEN_ID}" ]; then
     exit 1
 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.
 existing_key=
 keyfile=
 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 ||:
     if [ "${luks_type}" = "luks1" ] && ! luksmeta test -d "${DEV}" \
                                     && [ -z "${FRC}" ]; then
@@ -119,6 +131,13 @@ case "${KEY}" in
     ;;
 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 [ "${luks_type}" = "luks1" ] && ! luksmeta test -d "${DEV}"; then
     luksmeta init -d "${DEV}" ${FRC}
@@ -127,7 +146,7 @@ fi
 if ! clevis_luks_do_bind "${DEV}" "${SLT}" "${TOKEN_ID}" \
                          "${PIN}" "${CFG}" \
                          "${YES}" "" \
-                         "${existing_key}" "${keyfile}"; then
+                         "${existing_key}" "${keyfile}" "${EXISTING_TOKEN_ID}"; then
     echo "Error adding new binding to ${DEV}" >&2
     exit 1
 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
 
-*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
 
@@ -20,7 +20,7 @@ policy. This is accomplished with a simple command:
 
 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.
 3. Stores the Clevis JWE in the LUKS header.
 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* - :
   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
 
 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"
 
+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
 # is a numeric value within the specified range.
 valid_slot() {
@@ -292,9 +307,10 @@ clevis_luks_check_valid_key_or_keyfile() {
     local KEY="${2:-}"
     local KEYFILE="${3:-}"
     local SLT="${4:-}"
+    local EXISTING_TOKEN_ID="${5:-}"
 
     [ -z "${DEV}" ] && return 1
-    [ -z "${KEYFILE}" ] && [ -z "${KEY}" ] && return 1
+    [ -z "${EXISTING_TOKEN_ID}" ] && [ -z "${KEYFILE}" ] && [ -z "${KEY}" ] && return 1
 
     local extra_args
     extra_args="$([ -n "${SLT}" ] && printf -- '--key-slot %s' "${SLT}")"
@@ -303,6 +319,11 @@ clevis_luks_check_valid_key_or_keyfile() {
                    ${extra_args}
         return
     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}" \
                                       ${extra_args}
@@ -313,6 +334,7 @@ clevis_luks_check_valid_key_or_keyfile() {
 clevis_luks_unlock_device_by_slot() {
     local DEV="${1}"
     local SLT="${2}"
+    local SKIP_CHECK="${3}"
 
     [ -z "${DEV}" ] && return 1
     [ -z "${SLT}" ] && return 1
@@ -327,8 +349,9 @@ clevis_luks_unlock_device_by_slot() {
                        || [ -z "${passphrase}" ]; then
         return 1
     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}"
 }
 
@@ -336,6 +359,8 @@ clevis_luks_unlock_device_by_slot() {
 # parameter and returns the decoded passphrase.
 clevis_luks_unlock_device() {
     local DEV="${1}"
+    local SKIP_CHECK="YES"
+
     [ -z "${DEV}" ] && return 1
 
     local used_slots
@@ -346,7 +371,7 @@ clevis_luks_unlock_device() {
 
     local slt pt
     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
              continue
         fi
@@ -393,13 +418,18 @@ clevis_devices_to_unlock() {
     clevis_devices=
 
     # 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}") \
                    || [ -z "${dev}" ]; then
             # Unable to get the device - maybe it's not available, e.g. a
             # device on a volume group that has not been activated yet.
             # Add it to the list anyway, since it's a pending device.
-            clevis_devices="${clevis_devices} ${dev}"
+            clevis_devices="${clevis_devices} ${crypt_device}"
             continue
         fi
 
@@ -448,6 +478,12 @@ clevis_luks1_save_slot() {
         echo "Error saving metadata to LUKSMeta slot ${SLOT} from ${DEV}" >&2
         return 1
     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
 }
 
@@ -734,10 +770,11 @@ clevis_luks_add_key() {
     local NEWKEY="${3}"
     local KEY="${4}"
     local KEYFILE="${5:-}"
+    local EXISTING_TOKEN_ID="${6:-}"
 
     [ -z "${DEV}" ] && 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
     input="$(printf '%s\n%s' "${KEY}" "${NEWKEY}")"
@@ -745,10 +782,16 @@ clevis_luks_add_key() {
         extra_args="$(printf -- '--key-file %s' "${KEYFILE}")"
         input="$(printf '%s' "${NEWKEY}")"
     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 \
                                          --key-slot "${SLT}" \
                                          "${DEV}" \
+                                         ${pbkdf_args} \
                                          ${extra_args}
 }
 
@@ -759,6 +802,7 @@ clevis_luks_update_key() {
     local NEWKEY="${3}"
     local KEY="${4}"
     local KEYFILE="${5:-}"
+    local EXISTING_TOKEN_ID="${6:-}"
 
     [ -z "${DEV}" ] && return 1
     [ -z "${NEWKEY}" ] && return 1
@@ -768,7 +812,7 @@ clevis_luks_update_key() {
     local in_place
     clevis_luks_check_valid_key_or_keyfile "${DEV}" \
                                            "${KEY}" "${KEYFILE}" \
-                                           "${SLT}" 2>/dev/null \
+                                           "${SLT}" "${EXISTING_TOKEN_ID}" 2>/dev/null \
                                            && in_place=true
 
     local input extra_args=
@@ -777,11 +821,19 @@ clevis_luks_update_key() {
         extra_args="$(printf -- '--key-file %s' "${KEYFILE}")"
         input="$(printf '%s' "${NEWKEY}")"
     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
         printf '%s' "${input}" | cryptsetup luksChangeKey "${DEV}" \
                                             --key-slot "${SLT}" \
-                                            --batch-mode ${extra_args}
+                                            --batch-mode \
+                                            ${pbkdf_args} \
+                                            ${extra_args}
         return
     fi
 
@@ -803,6 +855,7 @@ clevis_luks_save_key_to_slot() {
     local KEY="${4}"
     local KEYFILE="${5:-}"
     local OVERWRITE="${6:-}"
+    local EXISTING_TOKEN_ID="${7:-}"
 
     [ -z "${DEV}" ] && return 1
     [ -z "${SLT}" ] && return 1
@@ -820,13 +873,13 @@ clevis_luks_save_key_to_slot() {
         [ -n "${OVERWRITE}" ] || return 1
 
         clevis_luks_update_key "${DEV}" "${SLT}" \
-                               "${NEWKEY}" "${KEY}" "${KEYFILE}"
+                               "${NEWKEY}" "${KEY}" "${KEYFILE}" "${EXISTING_TOKEN_ID}"
         return
     fi
 
     # Add a new key.
     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.
@@ -835,6 +888,7 @@ clevis_luks_generate_key() {
     [ -z "${DEV}" ] && return 1
 
     local dump filter bits
+    local MAX_ENTROPY_BITS=256  # Maximum allowed by pwmake.
     dump=$(cryptsetup luksDump "${DEV}")
     if cryptsetup isLuks --type luks1 "${DEV}"; then
         filter="$(echo "${dump}" | sed -rn 's|MK bits:[ \t]*([0-9]+)|\1|p')"
@@ -846,6 +900,9 @@ clevis_luks_generate_key() {
     fi
 
     bits="$(echo -n "${filter}" | sort -n | tail -n 1)"
+    if [ "${bits}" -gt "${MAX_ENTROPY_BITS}" ]; then
+        bits="${MAX_ENTROPY_BITS}"
+    fi
     pwmake "${bits}"
 }
 
@@ -918,15 +975,17 @@ clevis_luks_do_bind() {
     local OVERWRITE="${7:-}"
     local KEY="${8:-}"
     local KEYFILE="${9:-}"
+    local EXISTING_TOKEN_ID="${10:-}"
 
     [ -z "${DEV}" ] && return 1
     [ -z "${PIN}" ] && return 1
     [ -z "${CFG}" ] && return 1
 
-
     if ! clevis_luks_check_valid_key_or_keyfile "${DEV}" \
                                                 "${KEY}" \
                                                 "${KEYFILE}" \
+                                                "" \
+                                                "${EXISTING_TOKEN_ID}" \
                     && ! KEY="$(clevis_luks_get_existing_key "${DEV}" \
                                 "Enter existing LUKS password: " \
                                 "recover")"; then
@@ -941,12 +1000,16 @@ clevis_luks_do_bind() {
     fi
 
     # 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
     # metadata.
     local CLEVIS_TMP_DIR
-
+    mkdir -p "${TMPDIR:-/tmp}"
     if ! CLEVIS_TMP_DIR="$(mktemp -d)" || [ -z "${CLEVIS_TMP_DIR}" ]; then
         echo "Unable to create a a temporary dir for device backup/restore" >&2
         return 1
@@ -971,7 +1034,7 @@ clevis_luks_do_bind() {
 
     if ! clevis_luks_save_key_to_slot "${DEV}" "${SLT}" \
                                       "${newkey}" "${KEY}" "${KEYFILE}" \
-                                      "${OVERWRITE}"; then
+                                      "${OVERWRITE}" "${EXISTING_TOKEN_ID}"; then
         echo "Unable to save/update key slot; operation cancelled" >&2
         clevis_luks_restore_dev "${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.
-# Suppor is determined at build time.
+# Support is determined at build time.
 function clevis_luks_luks2_supported() {
     # We require cryptsetup >= 2.0.4 to fully support LUKSv2.
     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() {
     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
 fi
 
+mkdir -p "${TMPDIR:-/tmp}"
 if ! CLEVIS_EDIT_TMP="$(mktemp -d)" || [ -z "${CLEVIS_EDIT_TMP}" ]; then
     echo "Creating a temporary dir for editing binding failed" >&2
     exit 1
@@ -173,7 +174,7 @@ rm -rf "${CLEVIS_EDIT_TMP}"
 
 echo "Updating binding..."
 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
     exit 1
 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
     TOK="$(grep -E -B1 "^\s+Keyslot:\s+$SLT$" <<< "$dump" \
         | 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
 
 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
 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
 

+ 9 - 0
src/luks/meson.build

@@ -14,6 +14,15 @@ else
     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(
   input: 'clevis-luks-common-functions.in',
   output: 'clevis-luks-common-functions',

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

@@ -67,8 +67,11 @@ while true; do
     done
 
     [ "${loop}" != true ] && break
+
     # 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;
     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() {
-    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
     return 0
 }
@@ -30,7 +30,7 @@ depends() {
 }
 
 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_libdir_file "libtss2-tcti-device.so*"
 }

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

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

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

@@ -2,3 +2,4 @@ subdir('clevis')
 subdir('clevis-pin-tang')
 subdir('clevis-pin-tpm2')
 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.
 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',
   output: 'tests-common-functions',
   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('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
 # 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-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()
     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)

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

@@ -36,6 +36,12 @@ luks2_supported() {
     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.
 new_device() {
     local LUKS="${1}"
@@ -64,7 +70,7 @@ new_device() {
 
     fallocate -l64M "${DEV}"
     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}"
     # Caching the just-formatted device for possible reuse.
     cp -f "${DEV}" "${DEV_CACHED}"
@@ -88,7 +94,7 @@ new_device_keyfile() {
 
     fallocate -l64M "${DEV}"
     cryptsetup luksFormat --type "${LUKS}" --pbkdf pbkdf2 \
-        --pbkdf-force-iterations 1000 --batch-mode \
+        --pbkdf-force-iterations 1000 --key-size 512 --batch-mode \
         "${DEV}" "${KEYFILE}"
 }
 

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

@@ -42,10 +42,23 @@ DEV="${TMP}/luks2-device"
 new_device "luks2" "${DEV}"
 # Binding.
 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
 
 SLT=1
 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

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

@@ -22,11 +22,11 @@
 
 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
     exit 1
 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)

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

@@ -26,10 +26,10 @@ if ! cfg="$(jose fmt -j "$1" -Oo- 2>/dev/null)"; then
 fi
 
 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
-    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
 
 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: ':'
   )
 
+  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-test', find_program(join_paths(src, 'pin-test')), env: env)
+  test('pin-null', find_program(join_paths(src, 'pin-null')), env: env)
 else
   warning('Will not install sss pin due to missing dependencies!')
 endif

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

@@ -2,9 +2,9 @@
 
 trap 'exit' ERR
 
-e="$(echo -n hi | clevis encrypt test '{}')"
+e="$(echo -n hi | clevis encrypt null '{}')"
 d="$(echo -n "$e" | clevis decrypt)"
 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

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

@@ -1,24 +1,24 @@
 #!/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)"
 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)"
 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
 
-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)"
 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
 
-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
 
 ! 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)
         return NULL;
 
-    if (BN_zero(yy) <= 0)
+    if (BN_set_word(yy, 0) <= 0)
         return NULL;
 
     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)
         return NULL;
 
-    if (BN_zero(k) <= 0)
+    if (BN_set_word(k, 0) <= 0)
         return NULL;
 
     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) */
 
-            if (BN_zero(tmp) <= 0)
+            if (BN_set_word(tmp, 0) <= 0)
                 return NULL;
 
             if (BN_mod_sub(tmp, tmp, xi, pp, ctx) <= 0)

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

@@ -20,6 +20,7 @@
 #pragma once
 #include <jansson.h>
 #include <stdint.h>
+#include <sys/types.h>
 
 json_t *
 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")"
 
-url="$url/rec/$kid"
+rec_url="$url/rec/$kid"
 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
 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
 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
 
 read -r -d . hdr
@@ -99,15 +101,16 @@ if ! key="$(jose fmt -j- -Og clevis -g tpm2 -g key -Su- <<< "$jhd")"; then
 fi
 
 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
 fi
 
 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
 fi
 
+mkdir -p "${TMPDIR:-/tmp}"
 if ! TMP="$(mktemp -d)"; then
     echo "Creating a temporary dir for TPM files failed!" >&2
     exit 1
@@ -142,6 +145,7 @@ if [ -n "$fail" ]; then
     echo "Creating TPM2 primary key failed!" >&2
     exit 1
 fi
+tpm2_flushcontext -t
 
 case "$TPM2TOOLS_VERSION" in
     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
     exit 1
 fi
+tpm2_flushcontext -t
 
 case "$TPM2TOOLS_VERSION" in
     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
     exit 1
 fi
+tpm2_flushcontext -t
 
 (echo -n "$jwk$hdr."; /bin/cat) | jose jwe dec -k- -i-
 exit $?

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

@@ -67,6 +67,27 @@ if [ -t 0 ]; then
     exit 2
 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)"
 
 match='version="(.)\.'
@@ -76,26 +97,28 @@ if [[ $TPM2TOOLS_VERSION -lt 3 ]] || [[ $TPM2TOOLS_VERSION -gt 5 ]]; then
     exit 1
 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
 
 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
 
+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
 
 if ! jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"; then
@@ -144,6 +172,7 @@ if ! jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')"; then
     exit 1
 fi
 
+mkdir -p "${TMPDIR:-/tmp}"
 if ! TMP="$(mktemp -d)"; then
     echo "Creating a temporary dir for TPM files failed!" >&2
     exit 1
@@ -160,6 +189,7 @@ if [ -n "$fail" ]; then
     echo "Creating TPM2 primary key failed!" >&2
     exit 1
 fi
+tpm2_flushcontext -t
 
 policy_options=()
 if [ -n "$pcr_ids" ]; then
@@ -173,6 +203,7 @@ if [ -n "$pcr_ids" ]; then
             echo "Creating PCR hashes file failed!" >&2
             exit 1
         fi
+        tpm2_flushcontext -t
     else
         if ! jose b64 dec -i- -O "$TMP"/pcr.digest <<< "$pcr_digest"; then
             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!"
         exit 1
     fi
+    tpm2_flushcontext -t
 
     policy_options+=(-L "$TMP/pcr.policy")
 else
@@ -208,6 +240,7 @@ if [ -n "$fail" ]; then
     echo "Creating TPM2 object for jwk failed!" >&2
     exit 1
 fi
+tpm2_flushcontext -t
 
 if ! jwk_pub="$(jose b64 enc -I "$TMP"/jwk.pub)"; then
     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'],
-        'tpm2_createpolicy', 'tpm2_create', 'tpm2_load', 'tpm2_unseal']
+        'tpm2_createpolicy', 'tpm2_create', 'tpm2_flushcontext', 'tpm2_load',
+        'tpm2_unseal']
 
 all = true
 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
         return 1
     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.
@@ -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
 # 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
 if ! $(which clevis-pin-tpm2 >/dev/null 2>&1);