Browse Source

Import upstream version 13

Sergio Correia 6 months ago
parent
commit
a539f02c81

+ 5 - 6
.travis/install

@@ -1,6 +1,6 @@
 #!/bin/bash -ex
 
-COMMON="meson curl git make file bzip2 jose tang cryptsetup ${CC}"
+COMMON="meson curl git make file bzip2 jose tang cryptsetup jq ${CC}"
 
 case "${DISTRO}" in
 debian:*|ubuntu:*)
@@ -21,8 +21,8 @@ debian:*|ubuntu:*)
         sleep 5
     done
 
-    DEBIAN_FRONTEND=noninteractive apt-get install -y keyboard-configuration \
-        console-setup
+    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 \
@@ -36,9 +36,8 @@ debian:*|ubuntu:*)
 fedora:*)
     dnf -y clean all
     dnf -y --setopt=deltarpm=0 update
-    dnf -y install ${COMMON} pkgconfig openssl-devel jansson-devel \
-        findutils libjose-devel luksmeta libluksmeta-devel audit-libs-devel \
-        libudisks2-devel tpm2-tools desktop-file-utils
+    dnf -y install dnf-utils jq
+    dnf -y builddep clevis
     ;;
 
 centos:*)

+ 1 - 1
.travis/script

@@ -30,7 +30,7 @@ fi
 ninja=$(findexe ninja ninja-build)
 "${ninja}" test
 
-bash <(curl -s https://codecov.io/bash) 2>&1 \
+bash <(curl --retry 10 -s https://codecov.io/bash) 2>&1 \
        | grep -E -v "has arcs (to entry|from exit) block"
 
 # vim: set ts=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:

+ 10 - 1
README.md

@@ -175,7 +175,7 @@ user intervention.
 
 #### Unlocker: Clevis command
 
-A LUKSv1 device bound to a Clevis policy can also be unlocked by using the clevis
+A LUKS device bound to a Clevis policy can also be unlocked by using the clevis
 luks unlock command.
 
 ```bash
@@ -190,6 +190,15 @@ LUKS volumes can be unbound using the clevis luks unbind command. For example:
 $ sudo clevis luks unbind -d /dev/sda1 -s 1
 ```
 
+#### Listing pins bound to LUKS volumes
+
+The pins that are bound to a given LUKS volume can be listed using the clevis
+luks list command. For example:
+
+```bash
+$ sudo clevis luks list -d /dev/sda1
+```
+
 ## Installing Clevis
 
 Please don't install Clevis directly. Instead, use your preferred

+ 1 - 1
meson.build

@@ -1,4 +1,4 @@
-project('clevis', 'c', license: 'GPL3+', version: '12',
+project('clevis', 'c', license: 'GPL3+', version: '13',
         default_options: 'c_std=c99')
 
 libexecdir = join_paths(get_option('prefix'), get_option('libexecdir'))

+ 1 - 1
src/clevis.1.adoc

@@ -103,7 +103,7 @@ This command performs four steps:
 
 1. Creates a new key with the same entropy as the LUKS master key.
 2. Encrypts the new key with Clevis.
-3. Stores the Clevis JWE in the LUKS header with LUKSMeta.
+3. Stores the Clevis JWE in the LUKS header.
 4. Enables the new key for use with LUKS.
 
 This disk can now be unlocked with your existing password as well as with

+ 99 - 80
src/initramfs-tools/scripts/local-top/clevis.in

@@ -1,4 +1,4 @@
-#!/bin/bash -e
+#!/bin/bash
 #
 # Copyright (c) 2017 Red Hat, Inc.
 # Copyright (c) 2017 Shawn Rose
@@ -24,64 +24,91 @@
 #
 
 case $1 in
-prereqs) exit 0;;
+prereqs) exit 0 ;;
 esac
 
-# Return 0 if $pid has a file descriptor pointing to $name, 1 otherwise
-in_fds() {
-    local  pid="$1" name="$2" fd
+# Return fifo path or nothing if not found
+get_fifo_path() {
+    local pid="$1"
     for fd in /proc/$pid/fd/*; do
         if [ -e "$fd" ]; then
-            [ "$(readlink -f "$fd")" != "$name" ] || return 0
+            if [[ $(readlink -f "${fd}") == *"/cryptsetup/passfifo" ]]; then
+                readlink -f "${fd}"
+            fi
         fi
     done
-    return 1
 }
 
-# Print the PID of the askpass process with a file descriptor opened to
-# /lib/cryptsetup/passfifo if there is one.
+# Print the PID of the askpass process and fifo path with a file descriptor opened to
 get_askpass_pid() {
     psinfo=$(ps) # Doing this so I don't end up matching myself
     echo "$psinfo" | awk "/$cryptkeyscript/ { print \$1 }" | while read -r pid; do
-        if in_fds "$pid" "$PASSFIFO"; then
-            echo "$pid"
+        pf=$(get_fifo_path "${pid}")
+        if [[ $pf != "" ]]; then
+            echo "${pid} ${pf}"
             break
         fi
     done
 }
 
 luks1_decrypt() {
-    luksmeta load "$@" \
-        | clevis decrypt
+    local CRYPTTAB_SOURCE=$1
+    local PASSFIFO=$2
+    UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e
+    luksmeta show -d "$CRYPTTAB_SOURCE" | while read -r slot state uuid; do
+        [ "$state" == "active" ] || continue
+        [ "$uuid" == "$UUID" ] || continue
+
+        lml=$(luksmeta load -d "${CRYPTTAB_SOURCE}" -s "${slot}" -u "${UUID}")
+        [ $? -eq 0 ] || continue
+
+        decrypted=$(echo -n "${lml}" | clevis decrypt 2>/dev/null)
+        [ $? -eq 0 ] || continue
 
-    local rc
-    for rc in "${PIPESTATUS[@]}"; do
-        [ $rc -eq 0 ] || return $rc
+        # Fail safe
+        [ "$decrypted" != "" ] || continue
+
+        echo -n "${decrypted}" >"$PASSFIFO"
+        return 0
     done
-    return 0
+
+    return 1
 }
 
-luks2_jwe() {
-    # jose jwe fmt -c outputs extra \n, so clean it up
-    cryptsetup token export "$@" \
-        | jose fmt -j- -Og jwe -o- \
-        | jose jwe fmt -i- -c \
-        | tr -d '\n'
+luks2_decrypt() {
+    local CRYPTTAB_SOURCE=$1
+    local PASSFIFO=$2
+    cryptsetup luksDump "$CRYPTTAB_SOURCE" | sed -rn 's|^\s+([0-9]+): clevis|\1|p' | while read -r id; do
+        # jose jwe fmt -c outputs extra \n, so clean it up
+        cte=$(cryptsetup token export --token-id "$id" "$CRYPTTAB_SOURCE")
+        [ $? -eq 0 ] || continue
 
-    local rc
-    for rc in "${PIPESTATUS[@]}"; do
-        [ $rc -eq 0 ] || return $rc
+        josefmt=$(echo "${cte}" | jose fmt -j- -Og jwe -o-)
+        [ $? -eq 0 ] || continue
+
+        josejwe=$(echo "${josefmt}" | jose jwe fmt -i- -c)
+        [ $? -eq 0 ] || continue
+
+        jwe=$(echo "${josejwe}" | tr -d '\n')
+        [ $? -eq 0 ] || continue
+
+        decrypted=$(echo -n "${jwe}" | clevis decrypt 2>/dev/null)
+        [ $? -eq 0 ] || continue
+
+        # Fail safe
+        [ "$decrypted" != "" ] || continue
+
+        echo -n "${decrypted}" >"$PASSFIFO"
+        return 0
     done
-    return 0
+
+    return 1
 }
 
 # Wait for askpass, and then try and decrypt immediately. Just in case
 # there are multiple devices that need decrypting, this will loop
 # infinitely (The local-bottom script will kill this after decryption)
-clevisloop()
-{
-    set -e
-
+clevisloop() {
     # Set the path how we want it (Probably not all needed)
     PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin"
 
@@ -92,54 +119,47 @@ clevisloop()
         cryptkeyscript='\/lib\/cryptsetup\/askpass'
     fi
 
-    PASSFIFO='/usr/lib/cryptsetup/passfifo'
-
     OLD_CRYPTTAB_SOURCE=""
 
     while true; do
 
-        pid=$(get_askpass_pid)
-
         until [ "$pid" ] && [ -p "$PASSFIFO" ]; do
             sleep .1
-            pid=$(get_askpass_pid)
+            pid_fifo=$(get_askpass_pid)
+            pid=$(echo "${pid_fifo}" | cut -d' ' -f1)
+            PASSFIFO=$(echo "${pid_fifo}" | cut -d' ' -f2-)
         done
 
         # Import CRYPTTAB_SOURCE from the askpass process.
-        local "$(grep '^CRYPTTAB_SOURCE=' /proc/"$pid"/environ)"
+        local CRYPTTAB_SOURCE="$(cat /proc/${pid}/environ 2> /dev/null | \
+            tr '\0' '\n' | grep '^CRYPTTAB_SOURCE=' | cut -d= -f2)"
+        [ -n "$CRYPTTAB_SOURCE" ] || continue
 
         # Make sure that CRYPTTAB_SOURCE is actually a block device
         [ ! -b "$CRYPTTAB_SOURCE" ] && continue
 
+        sleep .1
         # Make the source has changed if needed
         [ "$CRYPTTAB_SOURCE" = "$OLD_CRYPTTAB_SOURCE" ] && continue
-
         OLD_CRYPTTAB_SOURCE="$CRYPTTAB_SOURCE"
 
-        UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e
         if cryptsetup isLuks --type luks1 "$CRYPTTAB_SOURCE"; then
             # If the device is not initialized, sliently skip it.
             luksmeta test -d "$CRYPTTAB_SOURCE" || continue
 
-            luksmeta show -d "$CRYPTTAB_SOURCE" | while read -r slot state uuid; do
-                [ "$state" == "active" ] || continue
-                [ "$uuid" == "$UUID" ] || continue
-
-                if pt="$(luks1_decrypt -d "$CRYPTTAB_SOURCE" -s "$slot" -u "$UUID")"; then
-                    echo -n "$pt" > "$PASSFIFO"
-                    break
-                fi
-            done
+            if luks1_decrypt "${CRYPTTAB_SOURCE}" "${PASSFIFO}"; then
+                echo "Unlocked ${CRYPTTAB_SOURCE} with clevis"
+            else
+                OLD_CRYPTTAB_SOURCE=""
+                sleep 5
+            fi
         elif cryptsetup isLuks --type luks2 "$CRYPTTAB_SOURCE"; then
-            cryptsetup luksDump "$CRYPTTAB_SOURCE" | sed -rn 's|^\s+([0-9]+): clevis|\1|p' | while read -r id; do
-                jwe="$(luks2_jwe --token-id "$id" "$CRYPTTAB_SOURCE")" \
-                    || continue
-
-                if pt="$(echo -n "$jwe" | clevis decrypt)"; then
-                    echo -n "$pt" > "$PASSFIFO"
-                    break
-                fi
-            done
+            if luks2_decrypt "${CRYPTTAB_SOURCE}" "${PASSFIFO}"; then
+                echo "Unlocked ${CRYPTTAB_SOURCE} with clevis"
+            else
+                OLD_CRYPTTAB_SOURCE=""
+                sleep 5
+            fi
         fi
         # Now that the current device has its password, let's sleep a
         # bit. This gives cryptsetup time to actually decrypt the
@@ -150,35 +170,34 @@ clevisloop()
 
 . /scripts/functions
 
-
 # This is a copy  of 'all_netbootable_devices/all_non_enslaved_devices' for
 # platforms that might not provide it.
 clevis_all_netbootable_devices() {
-    for device in /sys/class/net/* ; do
-            if [ ! -e "$device/flags" ]; then
-                    continue
-            fi
+    for device in /sys/class/net/*; do
+        if [ ! -e "$device/flags" ]; then
+            continue
+        fi
 
-            loop=$(($(cat "$device/flags") & 0x8 && 1 || 0))
-            bc=$(($(cat "$device/flags") & 0x2 && 1 || 0))
-            ptp=$(($(cat "$device/flags") & 0x10 && 1 || 0))
+        loop=$(($(cat "$device/flags") & 0x8 && 1 || 0))
+        bc=$(($(cat "$device/flags") & 0x2 && 1 || 0))
+        ptp=$(($(cat "$device/flags") & 0x10 && 1 || 0))
 
-            # Skip any device that is a loopback
-            if [ $loop = 1 ]; then
-                    continue
-            fi
+        # Skip any device that is a loopback
+        if [ $loop = 1 ]; then
+            continue
+        fi
 
-            # Skip any device that isn't a broadcast
-            # or point-to-point.
-            if [ $bc = 0 ] && [ $ptp = 0 ]; then
-                    continue
-            fi
+        # Skip any device that isn't a broadcast
+        # or point-to-point.
+        if [ $bc = 0 ] && [ $ptp = 0 ]; then
+            continue
+        fi
 
-            # Skip any enslaved device (has "master" link
-            # attribute on it)
-            device=$(basename "$device")
-            ip -o link show "$device" | grep -q -w master && continue
-            DEVICE="$DEVICE $device"
+        # Skip any enslaved device (has "master" link
+        # attribute on it)
+        device=$(basename "$device")
+        ip -o link show "$device" | grep -q -w master && continue
+        DEVICE="$DEVICE $device"
     done
     echo "$DEVICE"
 }
@@ -202,4 +221,4 @@ if eth_check; then
 fi
 
 clevisloop &
-echo $! > /run/clevis.pid
+echo $! >/run/clevis.pid

+ 3 - 3
src/luks/clevis-luks-bind.1.adoc

@@ -5,7 +5,7 @@ CLEVIS-LUKS-BIND(1)
 
 == NAME
 
-clevis-luks-bind - Bind a LUKSv1 device using the specified policy
+clevis-luks-bind - Bind a LUKS device using the specified policy
 
 == SYNOPSIS
 
@@ -13,7 +13,7 @@ clevis-luks-bind - Bind a LUKSv1 device using the specified policy
 
 == OVERVIEW
 
-The *clevis luks bind* command binds a LUKSv1 device using the specified
+The *clevis luks bind* command binds a LUKS device using the specified
 policy. This is accomplished with a simple command:
 
     $ clevis luks bind -d /dev/sda tang '{"url":...}'
@@ -22,7 +22,7 @@ This command performs four steps:
 
 1. Creates a new key with the same entropy as the LUKS master key.
 2. Encrypts the new key with Clevis.
-3. Stores the Clevis JWE in the LUKS header with LUKSMeta.
+3. Stores the Clevis JWE in the LUKS header.
 4. Enables the new key for use with LUKS.
 
 This disk can now be unlocked with your existing password as well as with

+ 283 - 0
src/luks/clevis-luks-common-functions

@@ -0,0 +1,283 @@
+#!/bin/bash -e
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2019 Red Hat, Inc.
+# Author: Sergio Correia <scorreia@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/>.
+#
+
+# 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() {
+    local SLT="${1}"
+    local MAX_SLOTS="${2}"
+    case "${SLT}" in
+        ''|*[!0-9]*)
+            return 1
+            ;;
+        *)
+            # We got an integer, now let's make sure it is within the
+            # supported range.
+            if [ "${SLT}" -ge "${MAX_SLOTS}" ]; then
+                return 1
+            fi
+            ;;
+    esac
+}
+
+# clevis_luks_read_slot() will read a particular slot of a given device, which
+# should be either LUKS1 or LUKS2. Returns 1 in case of failure; 0 in case of
+# success.
+clevis_luks_read_slot() {
+    local DEV="${1}"
+    local SLT="${2}"
+
+    if [ -z "${DEV}" ] || [ -z "${SLT}" ]; then
+        echo "Need both a device and a slot as arguments." >&2
+        return 1
+    fi
+
+    local DATA_CODED=''
+    local MAX_LUKS1_SLOTS=8
+    local MAX_LUKS2_SLOTS=32
+    if cryptsetup isLuks --type luks1 "${DEV}"; then
+        if ! valid_slot "${SLT}" "${MAX_LUKS1_SLOTS}"; then
+            echo "Please, provide a valid key slot number; 0-7 for LUKS1" >&2
+            return 1
+        fi
+
+        if ! luksmeta test -d "${DEV}"; then
+            echo "The ${DEV} device is not valid!" >&2
+            return 1
+        fi
+
+        local CLEVIS_UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
+        local uuid
+        # Pattern from luksmeta: active slot uuid.
+        read -r _ _ uuid <<< "$(luksmeta show -d "${DEV}" | grep "^${SLT} *")"
+
+        if [ "${uuid}" != ${CLEVIS_UUID}"" ]; then
+            echo "Not a clevis slot!" >&2
+            return 1
+        fi
+
+        if ! DATA_CODED="$(luksmeta load -d "${DEV}" -s "${SLT}")"; then
+            echo "Cannot load data from ${DEV} slot:${SLT}!" >&2
+            return 1
+        fi
+    elif cryptsetup isLuks --type luks2 "${DEV}"; then
+        if ! valid_slot "${SLT}" "${MAX_LUKS2_SLOTS}"; then
+            echo "Please, provide a valid key slot number; 0-31 for LUKS2" >&2
+            return 1
+        fi
+
+        local token_id
+        token_id=$(cryptsetup luksDump "${DEV}" \
+                    | grep -E -B1 "^\s+Keyslot:\s+${SLT}$" \
+                    | head -n 1 | sed -rn 's|^\s+([0-9]+): clevis|\1|p')
+        if [ -z "${token_id}" ]; then
+            echo "Cannot load data from ${DEV} slot:${SLT}. No token found!" >&2
+            return 1
+        fi
+
+        local token
+        token=$(cryptsetup token export --token-id "${token_id}" "${DEV}")
+        DATA_CODED=$(jose fmt -j- -Og jwe -o- <<< "${token}" \
+                     | jose jwe fmt -i- -c)
+
+        if [ -z "${DATA_CODED}" ]; then
+            echo "Cannot load data from ${DEV} slot:${SLT}!" >&2
+            return 1
+        fi
+    else
+        echo "${DEV} is not a supported LUKS device!" >&2
+        return 1
+    fi
+    echo "${DATA_CODED}"
+}
+
+# clevis_luks_used_slots() will return the list of used slots for a given LUKS
+# device.
+clevis_luks_used_slots() {
+    local DEV="${1}"
+
+    local slots
+    if cryptsetup isLuks --type luks1 "${DEV}"; then
+        readarray -t slots < <(cryptsetup luksDump "${DEV}" \
+            | sed -rn 's|^Key Slot ([0-7]): ENABLED$|\1|p')
+    elif cryptsetup isLuks --type luks2 "${DEV}"; then
+        readarray -t slots < <(cryptsetup luksDump "${DEV}" \
+            | sed -rn 's|^\s+([0-9]+): luks2$|\1|p')
+    else
+        echo "${DEV} is not a supported LUKS device!" >&2
+        return 1
+    fi
+    echo "${slots[@]}"
+}
+
+# clevis_luks_decode_jwe() will decode a given JWE.
+clevis_luks_decode_jwe() {
+    local jwe="${1}"
+
+    local coded
+    if ! coded=$(jose jwe fmt -i- <<< "${jwe}"); then
+        return 1
+    fi
+
+    coded=$(jose fmt -j- -g protected -u- <<< "${coded}" | tr -d '"')
+    jose b64 dec -i- <<< "${coded}"
+}
+
+# clevis_luks_print_pin_config() will print the config of a given pin; i.e.
+# for tang it will display the associated url address, and for tpm2, the
+# properties in place, like the hash, for instance.
+clevis_luks_print_pin_config() {
+    local P="${1}"
+    local decoded="${2}"
+
+    local content
+    if ! content="$(jose fmt -j- -g clevis -g "${P}" -o- <<< "${decoded}")" \
+                    || [ -z "${content}" ]; then
+        return 1
+    fi
+
+    local pin=
+    case "${P}" in
+    tang)
+        local url
+        url="$(jose fmt -j- -g url -u- <<< "${content}")"
+        pin=$(printf '{"url":"%s"}' "${url}")
+        printf "tang '%s'" "${pin}"
+        ;;
+    tpm2)
+        # Valid properties for tpm2 pin are the following:
+        # hash, key, pcr_bank, pcr_ids, pcr_digest.
+        local key
+        local value
+        for key in 'hash' 'key' 'pcr_bank' 'pcr_ids' 'pcr_digest'; do
+            if value=$(jose fmt -j- -g "${key}" -u- <<< "${content}"); then
+                pin=$(printf '%s,"%s":"%s"' "${pin}" "${key}" "${value}")
+            fi
+        done
+        # Remove possible leading comma.
+        pin=${pin/#,/}
+        printf "tpm2 '{%s}'" "${pin}"
+        ;;
+    sss)
+        local threshold
+        threshold=$(jose fmt -j- -Og t -o- <<< "${content}")
+        clevis_luks_process_sss_pin "${content}" "${threshold}"
+        ;;
+    *)
+        printf "unknown pin '%s'" "${P}"
+        ;;
+    esac
+}
+
+# clevis_luks_decode_pin_config() will receive a JWE and extract a pin config
+# from it.
+clevis_luks_decode_pin_config() {
+    local jwe="${1}"
+
+    local decoded
+    if ! decoded=$(clevis_luks_decode_jwe "${jwe}"); then
+        return 1
+    fi
+
+    local P
+    if ! P=$(jose fmt -j- -Og clevis -g pin -u- <<< "${decoded}"); then
+        return 1
+    fi
+
+    clevis_luks_print_pin_config "${P}" "${decoded}"
+}
+
+# clevis_luks_join_sss_cfg() will receive a list of configurations for a given
+# pin and returns it as list, in the format PIN [cfg1, cfg2, ..., cfgN].
+clevis_luks_join_sss_cfg() {
+    local pin="${1}"
+    local cfg="${2}"
+    cfg=$(echo "${cfg}" | tr -d "'" | sed -e 's/^,//')
+    printf '"%s":[%s]' "${pin}" "${cfg}"
+}
+
+# clevis_luks_process_sss_pin() will receive a JWE with information on the sss
+# pin config, and also its associated threshold, and will extract the info.
+clevis_luks_process_sss_pin() {
+    local jwe="${1}"
+    local threshold="${2}"
+
+    local sss_tang
+    local sss_tpm2
+    local sss
+    local pin_cfg
+    local pin
+    local cfg
+
+    local coded
+    for coded in $(jose fmt -j- -Og jwe -Af- <<< "${jwe}"| tr -d '"'); do
+        if ! pin_cfg="$(clevis_luks_decode_pin_config "${coded}")"; then
+            continue
+        fi
+        read -r pin cfg <<< "${pin_cfg}"
+        case "${pin}" in
+        tang)
+            sss_tang="${sss_tang},${cfg}"
+            ;;
+        tpm2)
+            sss_tpm2="${sss_tpm2},${cfg}"
+            ;;
+        sss)
+            sss=$(echo "${cfg}" | tr -d "'")
+            ;;
+        esac
+    done
+
+    cfg=
+    if [ -n "${sss_tang}" ]; then
+        cfg=$(clevis_luks_join_sss_cfg "tang" "${sss_tang}")
+    fi
+
+    if [ -n "${sss_tpm2}" ]; then
+        cfg="${cfg},"$(clevis_luks_join_sss_cfg "tpm2" "${sss_tpm2}")
+    fi
+
+    if [ -n "${sss}" ]; then
+        cfg=$(printf '%s,"sss":%s' "${cfg}" "${sss}")
+    fi
+
+    # Remove possible leading comma.
+    cfg=${cfg/#,/}
+    pin=$(printf '{"t":%d,"pins":{%s}}' "${threshold}" "${cfg}")
+    printf "sss '%s'" "${pin}"
+}
+
+# clevis_luks_read_pins_from_slot() will receive a given device and slot and
+# will then output its associated policy configuration.
+clevis_luks_read_pins_from_slot() {
+    local DEV="${1}"
+    local SLOT="${2}"
+
+    local jwe
+    if ! jwe=$(clevis_luks_read_slot "${DEV}" "${SLOT}" 2>/dev/null); then
+        return 1
+    fi
+
+    local cfg
+    if ! cfg="$(clevis_luks_decode_pin_config "${jwe}")"; then
+        return 1
+    fi
+    printf "%s: %s\n" "${SLOT}" "${cfg}"
+}

+ 77 - 0
src/luks/clevis-luks-list

@@ -0,0 +1,77 @@
+#!/bin/bash -e
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2017-2019 Red Hat, Inc.
+# Author: Javier Martinez Canillas <javierm@redhat.com>
+# Author: Sergio Correia <scorreia@redhat.com> - LUKS2 support.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+. clevis-luks-common-functions
+
+SUMMARY="Lists pins bound to a LUKSv1 or LUKSv2 device"
+
+function usage() {
+    echo >&2
+    echo "Usage: clevis luks list -d DEV [-s SLT]" >&2
+    echo >&2
+    echo "$SUMMARY": >&2
+    echo >&2
+    echo "  -d DEV  The LUKS device to list bound pins" >&2
+    echo >&2
+    echo "  -s SLOT The slot number to list" >&2
+    echo >&2
+    exit 1
+}
+
+if [ ${#} -eq 1 ] && [ "${1}" = "--summary" ]; then
+    echo "${SUMMARY}"
+    exit 0
+fi
+
+while getopts ":d:s:" o; do
+    case "$o" in
+    d) DEV=${OPTARG};;
+    s) SLT=${OPTARG};;
+    *) usage;;
+    esac
+done
+
+if [ -z "${DEV}" ]; then
+    echo "Did not specify a device!" >&2
+    usage
+fi
+
+if cryptsetup isLuks --type luks1 "${DEV}"; then
+    if ! luksmeta test -d "${DEV}" 2>/dev/null; then
+        echo "The ${DEV} device is not valid!" >&2
+        exit 1
+    fi
+fi
+
+if [ -n "${SLT}" ]; then
+    clevis_luks_read_pins_from_slot "${DEV}" "${SLT}"
+else
+    if ! used_slots=$(clevis_luks_used_slots "${DEV}"); then
+        echo "No used slots detected for device ${DEV}!" >&2
+        exit 1
+    fi
+
+    for s in ${used_slots}; do
+        if ! clevis_luks_read_pins_from_slot "${DEV}" "${s}"; then
+            continue
+        fi
+    done
+fi

+ 59 - 0
src/luks/clevis-luks-list.1.adoc

@@ -0,0 +1,59 @@
+CLEVIS-LUKS-LIST(1)
+===================
+:doctype: manpage
+
+
+== NAME
+
+clevis-luks-list - Lists pins bound to a LUKS device
+
+== SYNOPSIS
+
+*clevis luks list* -d DEV [-s SLT]
+
+== OVERVIEW
+
+The *clevis luks list* command list the pins bound to LUKS device.
+For example:
+
+    clevis luks list -d /dev/sda1
+
+== OPTIONS
+
+* *-d* _DEV_ :
+  The LUKS device on which to list bound pins
+
+* *-s* _SLT_ :
+  The slot to use for listing the pin from
+
+== EXAMPLES
+
+    clevis luks list -d /dev/sda1
+    1: sss '{"t":1,"pins":{"tang":[{"url":"addr1"},{"url":"addr2"}],"tpm2":[{"hash":"sha256","key":"ecc"}],"sss":{"t":1,"pins":{"tang":[{"url":"addr3"}]}}}}'
+    2: tang '{"url":"addr"}'
+    3: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha1","pcr_ids":"7"}'
+
+As we can see in the example above, */dev/sda1* has three slots bound each with a different pin.
+
+- Slot #1 is bound with the _sss_ pin, and uses also tang and tpm2 pins in its policy.
+- Slot #2 is bound using the _tang_ pin
+- Slot #3 is bound with the _tpm2_ pin
+
+Note that the output of *clevis luks list* can be used with the *clevis luks bind* command, such as:
+
+    clevis luks bind -d /dev/sda1 tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha1","pcr_ids":"7"}'
+
+And we will bind another slot with a policy similar to the one we have in slot #3.
+Also note that if you are interested in a particular slot, you can pass the _-s SLT_ argument to *clevis luks list*:
+
+  clevis luks list -d /dev/sda1 -s 2
+  2: tang '{"url":"addr"}'
+
+In the above example, we listed only the pin bound to slot #2.
+
+== SEE ALSO
+
+link:clevis-luks-bind.1.adoc[*clevis-luks-bind*(1)],
+link:clevis-encrypt-tang.1.adoc[*clevis-encrypt-tang*(1)],
+link:clevis-encrypt-tpm2.1.adoc[*clevis-encrypt-tpm2*(1)],
+link:clevis-encrypt-sss.1.adoc[*clevis-encrypt-sss*(1)],

+ 4 - 3
src/luks/clevis-luks-unbind.1.adoc

@@ -5,7 +5,7 @@ CLEVIS-LUKS-UNBIND(1)
 
 == NAME
 
-clevis-luks-unbind - Unbinds a pin bound to a LUKSv1 volume
+clevis-luks-unbind - Unbinds a pin bound to a LUKS volume
 
 == SYNOPSIS
 
@@ -13,7 +13,7 @@ clevis-luks-unbind - Unbinds a pin bound to a LUKSv1 volume
 
 == OVERVIEW
 
-The *clevis luks unbind* command unbinds a pin bound to a LUKSv1 volume.
+The *clevis luks unbind* command unbinds a pin bound to a LUKS volume.
 For example:
 
     $ clevis luks unbind -d /dev/sda -s 1
@@ -24,7 +24,8 @@ For example:
   The bound LUKS device
 
 * *-s* _SLT_ :
-  The LUKSMeta slot number for the pin to unbind
+  The slot number for the pin to unbind. When using LUKSv1, this is the
+  the LUKSmeta slot
 
 * *-f* :
   Do not ask for confirmation and wipe slot in batch-mode

+ 2 - 2
src/luks/clevis-luks-unlock.1.adoc

@@ -5,7 +5,7 @@ CLEVIS-LUKS-UNLOCK(1)
 
 == NAME
 
-clevis-luks-unlock - Unlocks a LUKSv1 device bound with a Clevis policy
+clevis-luks-unlock - Unlocks a LUKS device bound with a Clevis policy
 
 == SYNOPSIS
 
@@ -13,7 +13,7 @@ clevis-luks-unlock - Unlocks a LUKSv1 device bound with a Clevis policy
 
 == OVERVIEW
 
-The *clevis luks unlock* command unlocks a LUKSv1 device using its already
+The *clevis luks unlock* command unlocks a LUKS device using its already
 provisioned Clevis policy. For example:
 
     $ clevis luks unlock -d /dev/sda

+ 5 - 0
src/luks/meson.build

@@ -38,6 +38,11 @@ if libcryptsetup.found() and luksmeta.found() and pwmake.found()
   mans += join_paths(meson.current_source_dir(), 'clevis-luks-bind.1')
 
   mans += join_paths(meson.current_source_dir(), 'clevis-luks-unlockers.7')
+
+  bins += join_paths(meson.current_source_dir(), 'clevis-luks-common-functions')
+  bins += join_paths(meson.current_source_dir(), 'clevis-luks-list')
+  mans += join_paths(meson.current_source_dir(), 'clevis-luks-list.1')
+
 else
   warning('Will not install LUKS support due to missing dependencies!')
 endif

+ 85 - 0
src/luks/tests/list-recursive-luks1

@@ -0,0 +1,85 @@
+#!/bin/bash -ex
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2019 Red Hat, Inc.
+# Author: Sergio Correia <scorreia@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}"
+}
+
+trap 'on_exit' EXIT
+trap 'exit' ERR
+
+TMP="$(mktemp -d)"
+
+ADV="${TMP}/adv.jws"
+create_tang_adv "${ADV}"
+PIN="sss"
+CFG=$(printf '
+{
+  "t": 1,
+  "pins": {
+    "sss": {
+      "t": 1,
+      "pins": {
+        "sss": {
+          "t": 1,
+          "pins": {
+            "tang": [
+              {
+                "url": "ADDR","adv": "%s"
+              }
+            ]
+          }
+        }
+      }
+    }
+  }
+}
+' "${ADV}")
+
+# LUKS1.
+DEV="${TMP}/luks1-device"
+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
+new_device "luks1" "${DEV}"
+
+if ! clevis luks bind -f -d "${DEV}" "${PIN}" "${CFG}" <<< "${DEFAULT_PASS}"; then
+    error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password."
+fi
+
+SLT=1
+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then
+    error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})"
+fi
+
+if [[ "${slot}" != "${SLT}:" ]]; then
+    error "${TEST}: slot (${slot}) is expected to be ${SLT}"
+fi
+
+if [[ "${pin}" != "${PIN}" ]]; then
+    error "${TEST}: pin (${pin}) is expected to be '${PIN}'"
+fi
+
+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}")
+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/}
+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then
+    error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})"
+fi

+ 85 - 0
src/luks/tests/list-recursive-luks2

@@ -0,0 +1,85 @@
+#!/bin/bash -ex
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2019 Red Hat, Inc.
+# Author: Sergio Correia <scorreia@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}"
+}
+
+trap 'on_exit' EXIT
+trap 'exit' ERR
+
+TMP="$(mktemp -d)"
+
+ADV="${TMP}/adv.jws"
+create_tang_adv "${ADV}"
+PIN="sss"
+CFG=$(printf '
+{
+  "t": 1,
+  "pins": {
+    "sss": {
+      "t": 1,
+      "pins": {
+        "sss": {
+          "t": 1,
+          "pins": {
+            "tang": [
+              {
+                "url": "ADDR","adv": "%s"
+              }
+            ]
+          }
+        }
+      }
+    }
+  }
+}
+' "${ADV}")
+
+# LUKS2.
+DEV="${TMP}/luks1-device"
+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
+new_device "luks2" "${DEV}"
+
+if ! clevis luks bind -f -d "${DEV}" "${PIN}" "${CFG}" <<< "${DEFAULT_PASS}"; then
+    error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password."
+fi
+
+SLT=1
+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then
+    error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})"
+fi
+
+if [[ "${slot}" != "${SLT}:" ]]; then
+    error "${TEST}: slot (${slot}) is expected to be ${SLT}"
+fi
+
+if [[ "${pin}" != "${PIN}" ]]; then
+    error "${TEST}: pin (${pin}) is expected to be '${PIN}'"
+fi
+
+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}")
+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/}
+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then
+    error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})"
+fi

+ 77 - 0
src/luks/tests/list-sss-tang-luks1

@@ -0,0 +1,77 @@
+#!/bin/bash -ex
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2019 Red Hat, Inc.
+# Author: Sergio Correia <scorreia@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}"
+}
+
+trap 'on_exit' EXIT
+trap 'exit' ERR
+
+TMP="$(mktemp -d)"
+
+ADV="${TMP}/adv.jws"
+create_tang_adv "${ADV}"
+PIN="sss"
+CFG=$(printf '
+{
+   "t": 2,
+   "pins": {
+     "tang": [
+       {"url":"ADDR1","adv":"%s"},
+       {"url":"ADDR2","adv":"%s"},
+       {"url":"ADDR3","adv":"%s"},
+       {"url":"ADDR4","adv":"%s"},
+       {"url":"ADDR5","adv":"%s"}
+     ]
+   }
+}
+' "${ADV}" "${ADV}" "${ADV}" "${ADV}" "${ADV}")
+
+# LUKS1.
+DEV="${TMP}/luks1-device"
+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
+new_device "luks1" "${DEV}"
+
+if ! clevis luks bind -f -d "${DEV}" ${PIN} "${CFG}" <<< "${DEFAULT_PASS}"; then
+    error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password."
+fi
+
+SLT=1
+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then
+    error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})"
+fi
+
+if [[ "${slot}" != "${SLT}:" ]]; then
+    error "${TEST}: slot (${slot}) is expected to be ${SLT}"
+fi
+
+if [[ "${pin}" != "${PIN}" ]]; then
+    error "${TEST}: pin (${pin}) is expected to be '${PIN}'"
+fi
+
+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}")
+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/}
+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then
+    error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})"
+fi

+ 77 - 0
src/luks/tests/list-sss-tang-luks2

@@ -0,0 +1,77 @@
+#!/bin/bash -ex
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2019 Red Hat, Inc.
+# Author: Sergio Correia <scorreia@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}"
+}
+
+trap 'on_exit' EXIT
+trap 'exit' ERR
+
+TMP="$(mktemp -d)"
+
+ADV="${TMP}/adv.jws"
+create_tang_adv "${ADV}"
+PIN="sss"
+CFG=$(printf '
+{
+   "t": 2,
+   "pins": {
+     "tang": [
+       {"url":"ADDR1","adv":"%s"},
+       {"url":"ADDR2","adv":"%s"},
+       {"url":"ADDR3","adv":"%s"},
+       {"url":"ADDR4","adv":"%s"},
+       {"url":"ADDR5","adv":"%s"}
+     ]
+   }
+}
+' "${ADV}" "${ADV}" "${ADV}" "${ADV}" "${ADV}")
+
+# LUKS2.
+DEV="${TMP}/luks1-device"
+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
+new_device "luks2" "${DEV}"
+
+if ! clevis luks bind -f -d "${DEV}" ${PIN} "${CFG}" <<< "${DEFAULT_PASS}"; then
+    error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password."
+fi
+
+SLT=1
+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then
+    error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})"
+fi
+
+if [[ "${slot}" != "${SLT}:" ]]; then
+    error "${TEST}: slot (${slot}) is expected to be ${SLT}"
+fi
+
+if [[ "${pin}" != "${PIN}" ]]; then
+    error "${TEST}: pin (${pin}) is expected to be '${PIN}'"
+fi
+
+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}")
+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/}
+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then
+    error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})"
+fi

+ 64 - 0
src/luks/tests/list-tang-luks1

@@ -0,0 +1,64 @@
+#!/bin/bash -ex
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2019 Red Hat, Inc.
+# Author: Sergio Correia <scorreia@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}"
+}
+
+trap 'on_exit' EXIT
+trap 'exit' ERR
+
+TMP="$(mktemp -d)"
+
+ADV="${TMP}/adv.jws"
+create_tang_adv "${ADV}"
+PIN="tang"
+CFG=$(printf '{"url": "ADDR","adv": "%s"}' "${ADV}")
+
+# LUKS1.
+DEV="${TMP}/luks1-device"
+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
+new_device "luks1" "${DEV}"
+
+if ! clevis luks bind -f -d "${DEV}" "${PIN}" "${CFG}" <<< "${DEFAULT_PASS}"; then
+    error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password."
+fi
+
+SLT=1
+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then
+    error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})"
+fi
+
+if [[ "${slot}" != "${SLT}:" ]]; then
+    error "${TEST}: slot (${slot}) is expected to be ${SLT}"
+fi
+
+if [[ "${pin}" != "${PIN}" ]]; then
+    error "${TEST}: pin (${pin}) is expected to be '${PIN}'"
+fi
+
+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}")
+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/}
+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then
+    error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})"
+fi

+ 64 - 0
src/luks/tests/list-tang-luks2

@@ -0,0 +1,64 @@
+#!/bin/bash -ex
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2019 Red Hat, Inc.
+# Author: Sergio Correia <scorreia@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}"
+}
+
+trap 'on_exit' EXIT
+trap 'exit' ERR
+
+TMP="$(mktemp -d)"
+
+ADV="${TMP}/adv.jws"
+create_tang_adv "${ADV}"
+PIN="tang"
+CFG=$(printf '{"url": "ADDR","adv": "%s"}' "${ADV}")
+
+# LUKS2.
+DEV="${TMP}/luks1-device"
+UUID="cb6e8904-81ff-40da-a84a-07ab9ab5715e"
+new_device "luks2" "${DEV}"
+
+if ! clevis luks bind -f -d "${DEV}" "${PIN}" "${CFG}" <<< "${DEFAULT_PASS}"; then
+    error "${TEST}: Binding is expected to succeed when given a correct (${DEFAULT_PASS}) password."
+fi
+
+SLT=1
+if ! read -r slot pin cfg < <(clevis luks list -d "${DEV}" -s "${SLT}"); then
+    error "${TEST}: clevis luks list is expected to succeed for device(${DEV}) and slot (${SLT})"
+fi
+
+if [[ "${slot}" != "${SLT}:" ]]; then
+    error "${TEST}: slot (${slot}) is expected to be ${SLT}"
+fi
+
+if [[ "${pin}" != "${PIN}" ]]; then
+    error "${TEST}: pin (${pin}) is expected to be '${PIN}'"
+fi
+
+to_remove_from_cfg=$(printf ',"adv": "%s"' "${ADV}")
+cfg_for_cmp=${cfg//"${to_remove_from_cfg}"/}
+if ! pin_cfg_equal "${cfg}" "${cfg_for_cmp}"; then
+    error "${TEST}: config obtained from clevis luks list (${cfg}) is expected to match the one used to bind the test (${cfg_for_cmp})"
+fi

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

@@ -1,3 +1,6 @@
+# We use jq for comparing the pin config in the clevis luks list tests.
+jq = find_program('jq', required: false)
+
 common_functions = configure_file(input: 'tests-common-functions.in',
   output: 'tests-common-functions',
   configuration: luksmeta_data,
@@ -8,12 +11,16 @@ env = environment()
 env.prepend('PATH',
   join_paths(meson.source_root(), 'src'),
   join_paths(meson.source_root(), 'src', 'luks'),
+  join_paths(meson.source_root(), 'src', 'pins', 'sss'),
   join_paths(meson.source_root(), 'src', 'pins', 'tang'),
   join_paths(meson.source_root(), 'src', 'pins', 'tpm2'),
   meson.current_source_dir(),
   meson.current_build_dir(),
   join_paths(meson.build_root(), 'src'),
   join_paths(meson.build_root(), 'src', 'luks'),
+  join_paths(meson.build_root(), 'src', 'pins', 'sss'),
+  join_paths(meson.build_root(), 'src', 'pins', 'tang'),
+  join_paths(meson.build_root(), 'src', 'pins', 'tpm2'),
   separator: ':'
 )
 
@@ -27,6 +34,14 @@ test('bind-pass-with-newline-keyfile', find_program('bind-pass-with-newline-keyf
 # Bug #70.
 test('bind-already-used-luksmeta-slot', find_program('bind-already-used-luksmeta-slot'), env: env, timeout: 60)
 
+if jq.found()
+  test('list-recursive-luks1', find_program('list-recursive-luks1'), env: env)
+  test('list-tang-luks1', find_program('list-tang-luks1'), env: env)
+  test('list-sss-tang-luks1', find_program('list-sss-tang-luks1'), env: env)
+else
+  warning('Will not run "clevis luks list" tests due to missing jq dependency')
+endif
+
 # LUKS2 tests go here, and they get included if we get support for it, based
 # on the cryptsetup version.
 # Binding LUKS2 takes longer, so timeout is increased for a few tests.
@@ -35,4 +50,10 @@ if luksmeta_data.get('OLD_CRYPTSETUP') == '0'
   test('bind-luks2', find_program('bind-luks2'), env: env, timeout: 60)
   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 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)
+    test('list-sss-tang-luks2', find_program('list-sss-tang-luks2'), env: env, timeout: 60)
+  endif
 endif

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

@@ -104,4 +104,12 @@ new_device_keyfile() {
         "${DEV}" "${KEYFILE}"
 }
 
+pin_cfg_equal() {
+    local cfg1="${1}"
+    local cfg2="${1}"
+
+    diff <(jq -S . < <(echo -n "${cfg1}")) \
+         <(jq -S . < <(echo -n "${cfg2}"))
+}
+
 export DEFAULT_PASS='just-some-test-password-here'

+ 2 - 3
src/pins/sss/clevis-encrypt-sss.c

@@ -297,12 +297,11 @@ usage:
     fprintf(stderr, "\n");
     fprintf(stderr, "  pins: <object>   Pins used for encrypting fragments (REQUIRED)\n");
     fprintf(stderr, "\n");
-    fprintf(stderr, "Here is an example configuration for two of three servers:\n");
+    fprintf(stderr, "Here is an example configuration for one of two servers:\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "{\n");
-    fprintf(stderr, "  \"t\": 2,\n");
+    fprintf(stderr, "  \"t\": 1,\n");
     fprintf(stderr, "  \"pins\": {\n");
-    fprintf(stderr, "    \"http\": { \"url\": \"https://example.com/escrow/foo\" },\n");
     fprintf(stderr, "    \"tang\": [\n");
     fprintf(stderr, "      { \"url\": \"http://example.com/tang1\" },\n");
     fprintf(stderr, "      { \"url\": \"http://example.com/tang2\" }\n");

+ 9 - 5
src/pins/tpm2/clevis-encrypt-tpm2

@@ -103,17 +103,21 @@ key="$(jose fmt -j- -Og key -u- <<< "$cfg")" || key="ecc"
 
 pcr_bank="$(jose fmt -j- -Og pcr_bank -u- <<< "$cfg")" || pcr_bank="sha1"
 
+# Trim the spaces from the config, so that we will not have issues parsing
+# the PCR IDs.
+pcr_cfg=${cfg//[[:space:]]/}
 # Issue #103: We support passing pcr_ids using both a single string, as in
-# "1,3", as well as an actual JSON array, such as ["1,"3"]. Let's handle both
+# "1,3", as well as an actual JSON array, such as ["1","3"]. Let's handle both
 # cases here.
-if [[ ${cfg// /} != '{}' ]] \
-    && ! pcr_ids="$(jose fmt -j- -Og pcr_ids -u- 2>/dev/null <<< "$cfg")"; then
+if jose fmt -j- -Og pcr_ids 2>/dev/null <<< "${pcr_cfg}" \
+    && ! pcr_ids="$(jose fmt -j- -Og pcr_ids -u- 2>/dev/null \
+                    <<< "${pcr_cfg}")"; then
 
     # We failed to parse a string, so let's try to parse a JSON array instead.
-    if jose fmt -j- -Og pcr_ids -A 2>/dev/null <<< "${cfg}"; then
+    if jose fmt -j- -Og pcr_ids -A 2>/dev/null <<< "${pcr_cfg}"; then
         # OK, it is an array, so let's get the items and form a string.
         pcr_ids=
-        for pcr in $(jose fmt -j- -Og pcr_ids -Af- <<< "${cfg}" \
+        for pcr in $(jose fmt -j- -Og pcr_ids -Af- <<< "${pcr_cfg}" \
                      | tr -d '"'); do
             pcr_ids=$(printf '%s,%s' "${pcr_ids}" "${pcr}")
         done

+ 3 - 0
src/pins/tpm2/pin-tpm2

@@ -96,13 +96,16 @@ test_pcr_ids() {
 
 test_pcr_ids "${orig}" '{}' "" || exit 1
 test_pcr_ids "${orig}" '{                   }' "" || exit 1
+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