Skip to content

Commit ddbe5f9

Browse files
committed
Added TPM 1.2 support for initramfs-tools
Signed-off-by: Oldřich Jedlička <[email protected]>
1 parent add3e62 commit ddbe5f9

File tree

5 files changed

+284
-102
lines changed

5 files changed

+284
-102
lines changed

src/initramfs-tools/hooks/clevis.in

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,29 @@ find_binary() {
4747
echo "$resolved"
4848
}
4949

50+
find_library() {
51+
lib_name="$1"
52+
for lib_path in \
53+
{/usr,}/libexec/${lib_name} \
54+
{/usr,}/lib64/${lib_name} \
55+
{/usr,}/lib/${lib_name} \
56+
/usr/lib/`uname -m`-linux-gnu*/${lib_name} \
57+
; do
58+
if [ -e "$lib_path" ]; then
59+
echo "$lib_path"
60+
return
61+
fi
62+
done
63+
die 1 "Unable to find library ${lib_name}"
64+
}
65+
5066
if [ -n "${FORCE_CLEVIS}" ] && [ "${FORCE_CLEVIS}" != "n" ]; then
5167
for f in /sbin/cryptsetup /sbin/dmsetup /lib/cryptsetup/askpass; do
5268
if [ ! -e "${DESTDIR}${f}" ]; then
5369
die 2 "cryptsetup utility '$f' wasn't found in the generated ramdisk image. "
5470
fi
5571
done
56-
fi
72+
fi
5773

5874

5975
copy_exec @bindir@/clevis-decrypt-tang || die 1 "@bindir@/clevis-decrypt-tang not found"
@@ -80,6 +96,53 @@ if [ -x @bindir@/clevis-decrypt-tpm2 ]; then
8096
manual_add_modules tpm_crb
8197
manual_add_modules tpm_tis
8298
fi
99+
if [ -x @bindir@/clevis-decrypt-tpm1 ]; then
100+
copy_exec @bindir@/clevis-decrypt-tpm1 || die 1 "@bindir@/clevis-decrypt-tpm1 not found"
101+
copy_exec @libexecdir@/clevis-luks-tpm1-functions || die 1 "@libexecdir@/clevis-luks-tpm1-functions not found"
102+
103+
tcsd_bin=$(find_binary "tcsd")
104+
tpm_version_bin=$(find_binary "tpm_version")
105+
tpm_unsealdata_bin=$(find_binary "tpm_unsealdata")
106+
stdbuf_bin=$(find_binary "stdbuf")
107+
libstdbuf_bin=$(find_library "coreutils/libstdbuf.so*")
108+
109+
copy_exec "${tpm_version_bin}" || die 1 "Unable to copy ${tpm_version_bin}"
110+
copy_exec "${tpm_unsealdata_bin}" || die 1 "Unable to copy ${tpm_unsealdata_bin}"
111+
112+
copy_exec "${tcsd_bin}" || die 1 "Unable to copy ${tcsd_bin}"
113+
copy_file config /etc/tcsd.conf || dia 2 "Unable to copy /etc/tcsd.conf"
114+
115+
copy_exec "${stdbuf_bin}" || die 1 "Unable to copy ${stdbuf_bin}"
116+
copy_exec "${libstdbuf_bin}" || die 1 "Unable to copy ${libstdbuf_bin}"
117+
118+
mkdir -p "${DESTDIR}/var/lib/tpm" || die 2 "Unable to create /var/lib/tpm"
119+
cp /var/lib/tpm/* "${DESTDIR}/var/lib/tpm/" || die 2 "Unable to copy /var/lib/tpm"
120+
121+
mkdir -p "${DESTDIR}/lib/udev/rules.d" || die 2 "Unable to create /lib/udev/rules.d"
122+
# shellcheck disable=SC2043
123+
for rule in 60-tpm-udev.rules; do
124+
if [ -e /etc/udev/rules.d/$rule ]; then
125+
copy_file udev_rule /etc/udev/rules.d/$rule "/lib/udev/rules.d" || die 2 "Unable to copy $rule"
126+
elif [ -e /lib/udev/rules.d/$rule ]; then
127+
copy_file udev_rule /lib/udev/rules.d/$rule "/lib/udev/rules.d" || die 2 "Unable to copy $rule"
128+
fi
129+
done
130+
131+
echo "root:x:0:0:root:/root:/bin/bash" >> "${DESTDIR}/etc/passwd"
132+
echo "root:x:0:" >> "${DESTDIR}/etc/group"
133+
134+
group_id=`id -G tss` || die 2 "Unable to get tss group ID"
135+
user_id=`id -u tss` || die 2 "Unable to get tss user ID"
136+
echo "tss:x:$user_id:$group_id::/var/lib/tpm:/bin/false" >> "${DESTDIR}/etc/passwd"
137+
echo "tss:x:$group_id:" >> "${DESTDIR}/etc/group"
138+
139+
echo "127.0.0.1 localhost" >> "${DESTDIR}/etc/hosts"
140+
echo "::1 localhost ip6-localhost ip6-loopback" >> "${DESTDIR}/etc/hosts"
141+
echo "ff02::1 ip6-allnodes" >> "${DESTDIR}/etc/hosts"
142+
echo "ff02::2 ip6-allrouters" >> "${DESTDIR}/etc/hosts"
143+
144+
manual_add_modules tpm_tis
145+
fi
83146

84147

85148
luksmeta_bin=$(find_binary "luksmeta")

src/initramfs-tools/scripts/local-bottom/clevis.in

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,15 @@ esac
3333

3434
[ -s /run/clevis.pid ] || exit 0
3535

36+
if [ -f @libexecdir@/clevis-luks-tpm1-functions ]; then
37+
. @libexecdir@/clevis-luks-tpm1-functions
38+
stop_tcsd
39+
fi
40+
3641
pid=$(cat /run/clevis.pid)
3742
child_pids=$(ps -o pid,ppid | awk -v pid="$pid" '$2==pid { print $1 }')
3843
for kill_pid in $pid $child_pids; do
39-
kill "$kill_pid"
44+
kill "$kill_pid"
4045
done
4146

4247
# Not really worried about downing extra interfaces: they will come up

src/initramfs-tools/scripts/local-top/clevis.in

Lines changed: 115 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -28,87 +28,75 @@ prereqs) exit 0 ;;
2828
esac
2929

3030
# Return fifo path or nothing if not found
31-
get_fifo_path() {
31+
get_pid_fifo_path() {
3232
local pid="$1"
3333
for fd in /proc/$pid/fd/*; do
3434
if [ -e "$fd" ]; then
3535
if [[ $(readlink -f "${fd}") == *"/cryptsetup/passfifo" ]]; then
3636
readlink -f "${fd}"
37+
return 0
3738
fi
3839
fi
3940
done
41+
return 1
4042
}
4143

42-
# Print the PID of the askpass process and fifo path with a file descriptor opened to
43-
get_askpass_pid() {
44-
psinfo=$(ps) # Doing this so I don't end up matching myself
45-
echo "$psinfo" | awk "/$cryptkeyscript/ { print \$1 }" | while read -r pid; do
46-
pf=$(get_fifo_path "${pid}")
47-
if [[ $pf != "" ]]; then
48-
echo "${pid} ${pf}"
49-
break
50-
fi
51-
done
52-
}
44+
# Gets the luks device to be unlocked and used pins
45+
get_pid_device_pins() {
46+
local pid="$1"
47+
local CRYPTTAB_SOURCE
5348

54-
luks1_decrypt() {
55-
local CRYPTTAB_SOURCE=$1
56-
local PASSFIFO=$2
57-
UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e
58-
luksmeta show -d "$CRYPTTAB_SOURCE" | while read -r slot state uuid; do
59-
[ "$state" == "active" ] || continue
60-
[ "$uuid" == "$UUID" ] || continue
49+
CRYPTTAB_SOURCE=$(tr '\0' '\n' 2>/dev/null </proc/${pid}/environ | \
50+
grep '^CRYPTTAB_SOURCE=' | cut -d= -f2)
6151

62-
lml=$(luksmeta load -d "${CRYPTTAB_SOURCE}" -s "${slot}" -u "${UUID}")
63-
[ $? -eq 0 ] || continue
52+
# Wrong process, no CRYPTTAB_SOURCE, return error
53+
[ -n "$CRYPTTAB_SOURCE" ] || return 1
6454

65-
decrypted=$(echo -n "${lml}" | clevis decrypt 2>/dev/null)
66-
[ $? -eq 0 ] || continue
55+
[ -b "$CRYPTTAB_SOURCE" ] || return 0
6756

68-
# Fail safe
69-
[ "$decrypted" != "" ] || continue
57+
local cache="/var/cache/clevis-disks/${CRYPTTAB_SOURCE//\//_}"
58+
if [ ! -f "$cache" ]; then
59+
local pins=$(clevis_luks_used_pins "$CRYPTTAB_SOURCE")
60+
echo "${CRYPTTAB_SOURCE}:${pins}" > "$cache"
61+
fi
7062

71-
echo -n "${decrypted}" >"$PASSFIFO"
72-
return 0
73-
done
63+
cat "$cache"
64+
return 0
65+
}
7466

75-
return 1
67+
# Print colon-separated password-asking info like device, pins and fifo
68+
# path for unlocking with password
69+
get_askpass_info() {
70+
local cryptkeyscript=$1
71+
local psinfo pf dev_pins
72+
psinfo=$(ps -A) # Doing this so I don't end up matching myself
73+
echo "$psinfo" | awk "/$cryptkeyscript/ { print \$1 }" | {
74+
while read -r pid; do
75+
if pf=$(get_pid_fifo_path "${pid}") && dev_pins=$(get_pid_device_pins "${pid}"); then
76+
if [[ $pf != "" && $dev_pins != "" ]]; then
77+
# Output only in case of clevis device
78+
echo "${dev_pins}:${pf}"
79+
fi
80+
# Return that we found valid process
81+
return 0
82+
fi
83+
done
84+
return 1
85+
}
7686
}
7787

78-
luks2_decrypt() {
88+
# Try to decrypt the password to fifo file
89+
luks_decrypt() {
7990
local CRYPTTAB_SOURCE=$1
8091
local PASSFIFO=$2
81-
cryptsetup luksDump "$CRYPTTAB_SOURCE" | sed -rn 's|^\s+([0-9]+): clevis|\1|p' | while read -r id; do
82-
# jose jwe fmt -c outputs extra \n, so clean it up
83-
cte=$(cryptsetup token export --token-id "$id" "$CRYPTTAB_SOURCE")
84-
[ $? -eq 0 ] || continue
92+
local pt
8593

86-
josefmt=$(echo "${cte}" | jose fmt -j- -Og jwe -o-)
87-
[ $? -eq 0 ] || continue
88-
89-
josejwe=$(echo "${josefmt}" | jose jwe fmt -i- -c)
90-
[ $? -eq 0 ] || continue
91-
92-
jwe=$(echo "${josejwe}" | tr -d '\n')
93-
[ $? -eq 0 ] || continue
94-
95-
decrypted=$(echo -n "${jwe}" | clevis decrypt 2>/dev/null)
96-
[ $? -eq 0 ] || continue
97-
98-
# Fail safe
99-
[ "$decrypted" != "" ] || continue
100-
101-
echo -n "${decrypted}" >"$PASSFIFO"
94+
if pt=$(clevis_luks_unlock_device "${CRYPTTAB_SOURCE}"); then
95+
echo -n "${pt}" >"${PASSFIFO}"
10296
return 0
103-
done
104-
105-
return 1
106-
}
107-
108-
has_tang_pin() {
109-
local dev="$1"
110-
111-
clevis luks list -d "${dev}" | grep -q tang
97+
else
98+
return 1
99+
fi
112100
}
113101

114102
# Wait for askpass, and then try and decrypt immediately. Just in case
@@ -118,71 +106,69 @@ clevisloop() {
118106
# Set the path how we want it (Probably not all needed)
119107
PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin"
120108

109+
local cryptkeyscript
110+
local askpass_info
111+
local sleep_time
112+
local OLD_CRYPTTAB_SOURCE=""
113+
local netcfg_attempted=0
114+
local tpm1cfg_attempted=0
115+
121116
if [ -x /bin/plymouth ] && plymouth --ping; then
122117
cryptkeyscript='plymouth ask-for-password'
123118
else
124119
# This has to be escaped for awk
125120
cryptkeyscript='\/lib\/cryptsetup\/askpass'
126121
fi
127122

128-
OLD_CRYPTTAB_SOURCE=""
129-
local netcfg_attempted=0
130-
131123
while true; do
132-
133124
# Re-get the askpass PID in case there are multiple encrypted devices
134-
pid=""
135-
until [ "$pid" ] && [ -p "$PASSFIFO" ]; do
136-
sleep .1
137-
pid_fifo=$(get_askpass_pid)
138-
pid=$(echo "${pid_fifo}" | cut -d' ' -f1)
139-
PASSFIFO=$(echo "${pid_fifo}" | cut -d' ' -f2-)
125+
CRYPTTAB_SOURCE=""
126+
sleep_time=.1
127+
until [ -n "$CRYPTTAB_SOURCE" ] && [ -p "$PASSFIFO" ]; do
128+
sleep $sleep_time
129+
if askpass_info=$(get_askpass_info "$cryptkeyscript"); then
130+
# Workaround for initramfs-tools checking the script as sh-compatible
131+
IFS=':' read -r CRYPTTAB_SOURCE pins PASSFIFO <<EOM
132+
${askpass_info}
133+
EOM
134+
# Ask process is running, variables might be empty for non-clevis device
135+
sleep_time=.5
136+
else
137+
# No valid process found
138+
sleep_time=.1
139+
fi
140140
done
141141

142-
# Import CRYPTTAB_SOURCE from the askpass process.
143-
local CRYPTTAB_SOURCE="$(cat /proc/${pid}/environ 2> /dev/null | \
144-
tr '\0' '\n' | grep '^CRYPTTAB_SOURCE=' | cut -d= -f2)"
145-
[ -n "$CRYPTTAB_SOURCE" ] || continue
146-
147-
# Make sure that CRYPTTAB_SOURCE is actually a block device
148-
[ ! -b "$CRYPTTAB_SOURCE" ] && continue
149-
150-
sleep .1
151142
# Make the source has changed if needed
152143
[ "$CRYPTTAB_SOURCE" = "$OLD_CRYPTTAB_SOURCE" ] && continue
153144
OLD_CRYPTTAB_SOURCE="$CRYPTTAB_SOURCE"
154145

155-
if [ $netcfg_attempted -eq 0 ] && has_tang_pin ${CRYPTTAB_SOURCE}; then
146+
if [[ " $pins " == *" tang "* ]] && [ $netcfg_attempted -eq 0 ]; then
156147
netcfg_attempted=1
157148
do_configure_networking
158149
fi
150+
if [[ " $pins " == *" tpm1 "* ]] && [ $tpm1cfg_attempted -eq 0 ]; then
151+
tpm1cfg_attempted=1
152+
do_configure_tpm1
153+
fi
159154

160-
if cryptsetup isLuks --type luks1 "$CRYPTTAB_SOURCE"; then
161-
# If the device is not initialized, sliently skip it.
162-
luksmeta test -d "$CRYPTTAB_SOURCE" || continue
155+
if luks_decrypt "${CRYPTTAB_SOURCE}" "${PASSFIFO}"; then
156+
echo "Unlocked ${CRYPTTAB_SOURCE} with clevis"
163157

164-
if luks1_decrypt "${CRYPTTAB_SOURCE}" "${PASSFIFO}"; then
165-
echo "Unlocked ${CRYPTTAB_SOURCE} with clevis"
166-
else
167-
OLD_CRYPTTAB_SOURCE=""
168-
sleep 5
169-
fi
170-
elif cryptsetup isLuks --type luks2 "$CRYPTTAB_SOURCE"; then
171-
if luks2_decrypt "${CRYPTTAB_SOURCE}" "${PASSFIFO}"; then
172-
echo "Unlocked ${CRYPTTAB_SOURCE} with clevis"
173-
else
174-
OLD_CRYPTTAB_SOURCE=""
175-
sleep 5
176-
fi
158+
# Now that the current device has its password, let's sleep a
159+
# bit. This gives cryptsetup time to actually decrypt the
160+
# device and prompt for the next password if needed.
161+
sleep .5
162+
else
163+
# Unable to unlock now, retry later
164+
OLD_CRYPTTAB_SOURCE=""
165+
sleep 5
177166
fi
178-
# Now that the current device has its password, let's sleep a
179-
# bit. This gives cryptsetup time to actually decrypt the
180-
# device and prompt for the next password if needed.
181-
sleep .5
182167
done
183168
}
184169

185170
. /scripts/functions
171+
. clevis-luks-common-functions
186172

187173
# This is a copy of 'all_netbootable_devices/all_non_enslaved_devices' for
188174
# platforms that might not provide it.
@@ -229,9 +215,11 @@ get_specified_device() {
229215
# to appear. This code can be removed once that has been fixed in all the
230216
# distro releases we care about.
231217
wait_for_device() {
232-
local device="$(get_specified_device)"
218+
local device
233219
local ret=0
234220

221+
device=$(get_specified_device)
222+
235223
if [ -n "$device" ]; then
236224
log_begin_msg "clevis: Waiting for interface ${device} to become available"
237225
local netdev_wait=0
@@ -283,5 +271,32 @@ do_configure_networking() {
283271
fi
284272
}
285273

274+
do_configure_tpm1() {
275+
local tcsd_output=
276+
277+
[ -x @bindir@/clevis-decrypt-tpm1 ] && [ -f @libexecdir@/clevis-luks-tpm1-functions ] || return
278+
279+
. @libexecdir@/clevis-luks-tpm1-functions
280+
281+
log_begin_msg "clevis: Starting TCSD daemon"
282+
283+
wait_for_udev 10
284+
285+
if tcsd_output=$(start_tcsd 2>&1); then
286+
log_success_msg "clevis: TCSD up and running"
287+
else
288+
if [ -n "$tcsd_output" ]; then
289+
log_failure_msg "clevis: Unable to start TCSD: $tcsd_output"
290+
else
291+
log_failure_msg "clevis: Unable to start TCSD"
292+
fi
293+
fi
294+
295+
log_end_msg
296+
}
297+
298+
mkdir -p /var/cache/clevis-disks
299+
chmod 0700 /var/cache/clevis-disks
300+
286301
clevisloop &
287302
echo $! >/run/clevis.pid

0 commit comments

Comments
 (0)