Skip to content

Commit 71869cb

Browse files
authored
Add external token id for existing passphrase (#392)
This change introduces new parameter "-e", that allows specifying an existing token ID to avoid having to provide an existing passphrase and use an already configured LUKS2 token ID to read it Signed-off-by: Sergio Arroutbi <[email protected]> Signed-off-by: Sergio Arroutbi <[email protected]>
1 parent 3999199 commit 71869cb

File tree

8 files changed

+169
-15
lines changed

8 files changed

+169
-15
lines changed

.github/workflows/install-dependencies

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash -ex
22

3-
COMMON="meson curl git make file bzip2 jose tang cryptsetup jq socat ${CC}"
3+
COMMON="meson curl git make file bzip2 jose tang cryptsetup keyutils jq socat ${CC}"
44

55
case "${DISTRO}" in
66
debian:*|ubuntu:*)
@@ -33,7 +33,7 @@ fedora:*)
3333
printf 'max_parallel_downloads=10\nfastestmirror=1\n' >> /etc/dnf/dnf.conf
3434
dnf -y clean all
3535
dnf -y --setopt=deltarpm=0 update
36-
dnf -y install dnf-utils jq socat cryptsetup
36+
dnf -y install dnf-utils jq socat cryptsetup keyutils
3737
dnf -y builddep clevis
3838
;;
3939

src/luks/clevis-luks-bind

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ SUMMARY="Binds a LUKS device using the specified policy"
2525
usage() {
2626
exec >&2
2727
echo
28-
echo "Usage: clevis luks bind [-y] [-f] [-s SLT] [-k KEY] [-t TOKEN_ID] -d DEV PIN CFG"
28+
echo "Usage: clevis luks bind [-y] [-f] [-s SLT] [-k KEY] [-t TOKEN_ID] [-e EXISTING_TOKEN_ID] -d DEV PIN CFG"
2929
echo
3030
echo "$SUMMARY":
3131
echo
@@ -42,6 +42,8 @@ usage() {
4242
echo " -k KEY Non-interactively read LUKS password from KEY file"
4343
echo " -k - Non-interactively read LUKS password from standard input"
4444
echo
45+
echo " -e E_TKN_ID Existing LUKS token ID for existing passphrase; only available for LUKS2"
46+
echo
4547
exit 2
4648
}
4749

@@ -52,13 +54,14 @@ fi
5254

5355
FRC=
5456
YES=
55-
while getopts ":hfyd:s:k:t:" o; do
57+
while getopts ":hfyd:s:k:t:e:" o; do
5658
case "$o" in
5759
f) FRC='-f';;
5860
d) DEV="$OPTARG";;
5961
s) SLT="$OPTARG";;
6062
k) KEY="$OPTARG";;
6163
t) TOKEN_ID="$OPTARG";;
64+
e) EXISTING_TOKEN_ID="$OPTARG";;
6265
y) FRC='-f'
6366
YES='-y';;
6467
*) usage;;
@@ -99,11 +102,20 @@ if [ "${luks_type}" = "luks1" ] && [ -n "${TOKEN_ID}" ]; then
99102
exit 1
100103
fi
101104

105+
if [ -n "${EXISTING_TOKEN_ID}" ] && ! clevis_luks_luks2_existing_token_id_supported; then
106+
echo "Existing token ID not supported in this cryptsetup version" >&2
107+
exit 1
108+
fi
109+
102110
# Get the existing passphrase/keyfile.
103111
existing_key=
104112
keyfile=
105113
case "${KEY}" in
106-
"") IFS= read -r -s -p "Enter existing LUKS password: " existing_key; echo >&2;;
114+
"")
115+
if [ -z "${EXISTING_TOKEN_ID}" ] ; then
116+
IFS= read -r -s -p "Enter existing LUKS password: " existing_key; echo >&2
117+
fi
118+
;;
107119
-) IFS= read -r -s -p "" existing_key ||:
108120
if [ "${luks_type}" = "luks1" ] && ! luksmeta test -d "${DEV}" \
109121
&& [ -z "${FRC}" ]; then
@@ -119,6 +131,13 @@ case "${KEY}" in
119131
;;
120132
esac
121133

134+
# Check if existing token id for keyring read is provided
135+
# If so, keyfile is not allowed
136+
if [ -n "${EXISTING_TOKEN_ID}" ] && [ -n "${keyfile}" ] ; then
137+
echo "Cannot specify kernel keyring description together with key file" >&2
138+
exit 1
139+
fi
140+
122141
# If necessary, initialize the LUKS volume.
123142
if [ "${luks_type}" = "luks1" ] && ! luksmeta test -d "${DEV}"; then
124143
luksmeta init -d "${DEV}" ${FRC}
@@ -127,7 +146,7 @@ fi
127146
if ! clevis_luks_do_bind "${DEV}" "${SLT}" "${TOKEN_ID}" \
128147
"${PIN}" "${CFG}" \
129148
"${YES}" "" \
130-
"${existing_key}" "${keyfile}"; then
149+
"${existing_key}" "${keyfile}" "${EXISTING_TOKEN_ID}"; then
131150
echo "Error adding new binding to ${DEV}" >&2
132151
exit 1
133152
fi

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ clevis-luks-bind - Bind a LUKS device using the specified policy
99

1010
== SYNOPSIS
1111

12-
*clevis luks bind* [-f] [-y] -d DEV [-t TKN_ID] [-s SLT] [-k KEY] PIN CFG
12+
*clevis luks bind* [-f] [-y] -d DEV [-t TKN_ID] [-s SLT] [-k KEY] [-e EXISTING_TOKEN_ID] PIN CFG
1313

1414
== OVERVIEW
1515

@@ -54,6 +54,12 @@ Clevis LUKS unlockers. See link:clevis-luks-unlockers.7.adoc[*clevis-luks-unlock
5454
* *-k* - :
5555
Non-interactively read LUKS password from standard input
5656

57+
* *-e* _E_TKN_ID_ :
58+
LUKS token ID for existing passphrase; only available for LUKS2.
59+
This parameter allows providing a configured token ID in LUKS2
60+
containing the existing passphrase for this device, so that
61+
existing passphrase is not prompted by clevis
62+
5763
== CAVEATS
5864

5965
This command does not change the LUKS master key. This implies that if you

src/luks/clevis-luks-common-functions.in

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,10 @@ clevis_luks_check_valid_key_or_keyfile() {
307307
local KEY="${2:-}"
308308
local KEYFILE="${3:-}"
309309
local SLT="${4:-}"
310+
local EXISTING_TOKEN_ID="${5:-}"
310311

311312
[ -z "${DEV}" ] && return 1
312-
[ -z "${KEYFILE}" ] && [ -z "${KEY}" ] && return 1
313+
[ -z "${EXISTING_TOKEN_ID}" ] && [ -z "${KEYFILE}" ] && [ -z "${KEY}" ] && return 1
313314

314315
local extra_args
315316
extra_args="$([ -n "${SLT}" ] && printf -- '--key-slot %s' "${SLT}")"
@@ -318,6 +319,11 @@ clevis_luks_check_valid_key_or_keyfile() {
318319
${extra_args}
319320
return
320321
fi
322+
if [ -n "${EXISTING_TOKEN_ID}" ]; then
323+
cryptsetup open --test-passphrase "${DEV}" --token-id "${EXISTING_TOKEN_ID}" \
324+
${extra_args}
325+
return
326+
fi
321327

322328
printf '%s' "${KEY}" | cryptsetup open --test-passphrase "${DEV}" \
323329
${extra_args}
@@ -764,17 +770,22 @@ clevis_luks_add_key() {
764770
local NEWKEY="${3}"
765771
local KEY="${4}"
766772
local KEYFILE="${5:-}"
773+
local EXISTING_TOKEN_ID="${6:-}"
767774

768775
[ -z "${DEV}" ] && return 1
769776
[ -z "${NEWKEY}" ] && return 1
770-
[ -z "${KEY}" ] && [ -z "${KEYFILE}" ] && return 1
777+
[ -z "${EXISTING_TOKEN_ID}" ] && [ -z "${KEY}" ] && [ -z "${KEYFILE}" ] && return 1
771778

772779
local extra_args='' input
773780
input="$(printf '%s\n%s' "${KEY}" "${NEWKEY}")"
774781
if [ -n "${KEYFILE}" ]; then
775782
extra_args="$(printf -- '--key-file %s' "${KEYFILE}")"
776783
input="$(printf '%s' "${NEWKEY}")"
777784
fi
785+
if [ -n "${EXISTING_TOKEN_ID}" ]; then
786+
extra_args="$(printf -- '--token-id %s' "${EXISTING_TOKEN_ID}")"
787+
input="$(printf '%s' "${NEWKEY}")"
788+
fi
778789
local pbkdf_args="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
779790

780791
printf '%s' "${input}" | cryptsetup luksAddKey --batch-mode \
@@ -791,6 +802,7 @@ clevis_luks_update_key() {
791802
local NEWKEY="${3}"
792803
local KEY="${4}"
793804
local KEYFILE="${5:-}"
805+
local EXISTING_TOKEN_ID="${6:-}"
794806

795807
[ -z "${DEV}" ] && return 1
796808
[ -z "${NEWKEY}" ] && return 1
@@ -800,7 +812,7 @@ clevis_luks_update_key() {
800812
local in_place
801813
clevis_luks_check_valid_key_or_keyfile "${DEV}" \
802814
"${KEY}" "${KEYFILE}" \
803-
"${SLT}" 2>/dev/null \
815+
"${SLT}" "${EXISTING_TOKEN_ID}" 2>/dev/null \
804816
&& in_place=true
805817

806818
local input extra_args=
@@ -809,6 +821,11 @@ clevis_luks_update_key() {
809821
extra_args="$(printf -- '--key-file %s' "${KEYFILE}")"
810822
input="$(printf '%s' "${NEWKEY}")"
811823
fi
824+
if [ -n "${EXISTING_TOKEN_ID}" ]; then
825+
extra_args="$(printf -- '--token-id %s' "${EXISTING_TOKEN_ID}")"
826+
input="$(printf '%s' "${NEWKEY}")"
827+
fi
828+
812829
local pbkdf_args="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
813830

814831
if [ -n "${in_place}" ]; then
@@ -838,6 +855,7 @@ clevis_luks_save_key_to_slot() {
838855
local KEY="${4}"
839856
local KEYFILE="${5:-}"
840857
local OVERWRITE="${6:-}"
858+
local EXISTING_TOKEN_ID="${7:-}"
841859

842860
[ -z "${DEV}" ] && return 1
843861
[ -z "${SLT}" ] && return 1
@@ -855,13 +873,13 @@ clevis_luks_save_key_to_slot() {
855873
[ -n "${OVERWRITE}" ] || return 1
856874

857875
clevis_luks_update_key "${DEV}" "${SLT}" \
858-
"${NEWKEY}" "${KEY}" "${KEYFILE}"
876+
"${NEWKEY}" "${KEY}" "${KEYFILE}" "${EXISTING_TOKEN_ID}"
859877
return
860878
fi
861879

862880
# Add a new key.
863881
clevis_luks_add_key "${DEV}" "${SLT}" \
864-
"${NEWKEY}" "${KEY}" "${KEYFILE}"
882+
"${NEWKEY}" "${KEY}" "${KEYFILE}" "${EXISTING_TOKEN_ID}"
865883
}
866884

867885
# clevis_luks_generate_key() generates a new key for use with clevis.
@@ -957,15 +975,17 @@ clevis_luks_do_bind() {
957975
local OVERWRITE="${7:-}"
958976
local KEY="${8:-}"
959977
local KEYFILE="${9:-}"
978+
local EXISTING_TOKEN_ID="${10:-}"
960979

961980
[ -z "${DEV}" ] && return 1
962981
[ -z "${PIN}" ] && return 1
963982
[ -z "${CFG}" ] && return 1
964983

965-
966984
if ! clevis_luks_check_valid_key_or_keyfile "${DEV}" \
967985
"${KEY}" \
968986
"${KEYFILE}" \
987+
"" \
988+
"${EXISTING_TOKEN_ID}" \
969989
&& ! KEY="$(clevis_luks_get_existing_key "${DEV}" \
970990
"Enter existing LUKS password: " \
971991
"recover")"; then
@@ -1014,7 +1034,7 @@ clevis_luks_do_bind() {
10141034

10151035
if ! clevis_luks_save_key_to_slot "${DEV}" "${SLT}" \
10161036
"${newkey}" "${KEY}" "${KEYFILE}" \
1017-
"${OVERWRITE}"; then
1037+
"${OVERWRITE}" "${EXISTING_TOKEN_ID}"; then
10181038
echo "Unable to save/update key slot; operation cancelled" >&2
10191039
clevis_luks_restore_dev "${CLEVIS_TMP_DIR}" || :
10201040
rm -rf "${CLEVIS_TMP_DIR}"
@@ -1035,12 +1055,19 @@ clevis_luks_do_bind() {
10351055
}
10361056

10371057
# clevis_luks_luks2_supported() indicates whether we support LUKS2 devices.
1038-
# Suppor is determined at build time.
1058+
# Support is determined at build time.
10391059
function clevis_luks_luks2_supported() {
10401060
# We require cryptsetup >= 2.0.4 to fully support LUKSv2.
10411061
return @OLD_CRYPTSETUP@
10421062
}
10431063

1064+
# clevis_luks_luks2_existing_token_id_supported() indicates whether
1065+
# cryptsetup allows token id for passphrase providing
1066+
function clevis_luks_luks2_existing_token_id_supported() {
1067+
# We require cryptsetup >= 2.6.0 to fully support LUKSv2 addkey/open by token ID
1068+
return @OLD_CRYPTSETUP_EXISTING_TOKEN_ID@
1069+
}
1070+
10441071
# clevis_luks_type() returns the LUKS type of a device, e.g. "luks1".
10451072
clevis_luks_type() {
10461073
local DEV="${1}"

src/luks/meson.build

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ else
1414
endif
1515
endif
1616

17+
libcryptsetup_ext_token_id = dependency('libcryptsetup', version: '>=2.6.0', required: false)
18+
if libcryptsetup_ext_token_id.found()
19+
luksmeta_data.set('OLD_CRYPTSETUP_EXISTING_TOKEN_ID', '0')
20+
message('cryptsetup version supports existing token id')
21+
else
22+
luksmeta_data.set('OLD_CRYPTSETUP_EXISTING_TOKEN_ID', '1')
23+
warning('cryptsetup version does not support existing token id')
24+
endif
25+
1726
clevis_luks_common_functions = configure_file(
1827
input: 'clevis-luks-common-functions.in',
1928
output: 'clevis-luks-common-functions',
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/bin/bash -ex
2+
# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
3+
#
4+
# Copyright (c) 2022 Red Hat, Inc.
5+
# Author: Sergio Arroutbi <[email protected]>
6+
#
7+
# This program is free software: you can redistribute it and/or modify
8+
# it under the terms of the GNU General Public License as published by
9+
# the Free Software Foundation, either version 3 of the License, or
10+
# (at your option) any later version.
11+
#
12+
# This program is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU General Public License
18+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
#
20+
21+
TEST=$(basename "${0}")
22+
. tests-common-functions
23+
24+
on_exit() {
25+
[ -d "${TMP}" ] && rm -rf "${TMP}"
26+
}
27+
28+
create_existing_token_id_from_keyring() {
29+
local DEV="${1}"
30+
local KEYDESC="${2}"
31+
local TOKEN_ID="${3}"
32+
local PASS="${4}"
33+
if [[ -z "${DEV}" ]] || [[ -z "${KEYDESC}" ]] || [[ -z "${TOKEN_ID}" ]]; then
34+
return 1
35+
fi
36+
KEYRING_ID=$(keyctl add user "${KEYDESC}" "${PASS}" @s)
37+
keyctl print "${KEYRING_ID}" 2>/dev/null 1>/dev/null
38+
cryptsetup token add --token-id "${TOKEN_ID}" --key-description "${KEYDESC}" "${DEV}"
39+
}
40+
41+
if ! luks2_supported; then
42+
skip_test "${TEST}: LUKS2 is not supported."
43+
fi
44+
45+
if ! luks2_existing_token_id_supported; then
46+
skip_test "${TEST}: Existing token ID not supported"
47+
fi
48+
49+
trap 'on_exit' EXIT
50+
trap 'exit' ERR
51+
52+
TMP="$(mktemp -d)"
53+
54+
ADV="${TMP}/adv.jws"
55+
tang_create_adv "${TMP}" "${ADV}"
56+
CFG="$(printf '{"url":"foobar","adv":"%s"}' "$ADV")"
57+
58+
EXISTING_TOKEN_ID=5
59+
KEYDESC="testkey"
60+
PASS="123exttokenid_"
61+
DEV="${TMP}/luks2-device-ext-token"
62+
new_device "luks2" "${DEV}" "${PASS}"
63+
64+
create_existing_token_id_from_keyring "${DEV}" "${KEYDESC}" "${EXISTING_TOKEN_ID}" "${PASS}"
65+
66+
if ! clevis luks bind -y -d "${DEV}" -e "${EXISTING_TOKEN_ID}" tang "${CFG}"; then
67+
error "${TEST}: Binding expected to succeed with existing token id:${EXISTING_TOKEN_ID}" >&2
68+
fi
69+
70+
KEYFILE="${TMP}/keyfile.txt"
71+
touch "${KEYFILE}"
72+
if clevis luks bind -y -d "${DEV}" -e "${EXISTING_TOKEN_ID}" -k "${KEYFILE}" tang "${CFG}"; then
73+
error "${TEST}: Using existing token id and keyfile should dump an error" >&2
74+
fi

src/luks/tests/meson.build

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ jq = find_program('jq', required: false)
55
# given token slot.
66
cryptsetup = find_program('cryptsetup', required: true)
77

8+
# Use keyctl to check an existing token id can be created from
9+
# kernel keyring password
10+
keyutils = find_program('keyctl', required: false)
11+
if keyutils.found()
12+
message('keyutils installed')
13+
else
14+
warning('keyutils not installed, unable to test existing token id binding')
15+
endif
16+
817
common_functions = configure_file(input: 'tests-common-functions.in',
918
output: 'tests-common-functions',
1019
configuration: luksmeta_data,
@@ -70,6 +79,10 @@ if luksmeta_data.get('OLD_CRYPTSETUP') == '0'
7079
test('unbind-unbound-slot-luks2', find_program('unbind-unbound-slot-luks2'), env: env)
7180
test('unbind-luks2', find_program('unbind-luks2'), env: env, timeout: 60)
7281

82+
if keyutils.found() and luksmeta_data.get('OLD_CRYPTSETUP_EXISTING_TOKEN_ID') == '0'
83+
test('bind-luks2-ext-token', find_program('bind-luks2-ext-token'), env: env, timeout: 60)
84+
endif
85+
7386
if jq.found()
7487
test('list-recursive-luks2', find_program('list-recursive-luks2'), env: env, timeout: 60)
7588
test('list-tang-luks2', find_program('list-tang-luks2'), env: env, timeout: 60)

src/luks/tests/tests-common-functions.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ luks2_supported() {
3636
return @OLD_CRYPTSETUP@
3737
}
3838

39+
# We require cryptsetup >= 2.6.0 to fully support LUKSv2 addkey/open by token ID
40+
# Support is determined at build time.
41+
luks2_existing_token_id_supported() {
42+
return @OLD_CRYPTSETUP_EXISTING_TOKEN_ID@
43+
}
44+
3945
# Creates a new LUKS1 or LUKS2 device to be used.
4046
new_device() {
4147
local LUKS="${1}"

0 commit comments

Comments
 (0)